Add a packed TargetKconfig of device and accel CONFIG_* bits, generated from the Kconfig tree, and attach it to TargetInfo. target_kconfig() returns the current bits, and target_has_kconfig_<name>() tests them. Initialize target_info_stub.kconfig from CONFIG_DEVICES.
Signed-off-by: Yonggang Luo <[email protected]> --- include/qemu/target-info-def.h | 2 + include/qemu/target-kconfig-has.h | 7 ++ include/qemu/target-kconfig.h | 34 ++++++ meson.build | 14 +++ scripts/gen-target-kconfig.py | 162 +++++++++++++++++++++++++++ target-info-def.c | 6 + target-kconfig.c | 20 ++++ tests/unit/meson.build | 1 + tests/unit/test-target-kconfig-has.c | 22 ++++ 9 files changed, 268 insertions(+) create mode 100644 include/qemu/target-kconfig-has.h create mode 100644 include/qemu/target-kconfig.h create mode 100644 scripts/gen-target-kconfig.py create mode 100644 target-kconfig.c create mode 100644 tests/unit/test-target-kconfig-has.c diff --git a/include/qemu/target-info-def.h b/include/qemu/target-info-def.h index 080e30a5b0e..ae6374583a7 100644 --- a/include/qemu/target-info-def.h +++ b/include/qemu/target-info-def.h @@ -13,6 +13,7 @@ #include "qapi/qapi-types-machine.h" #include "qemu/queue.h" #include "qemu/target-info.h" +#include "qemu/target-kconfig.h" struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ @@ -32,6 +33,7 @@ struct TargetInfo { unsigned page_bits_init; /* runtime equivalent of TARGET_PAGE_BITS_VARY definition */ bool page_bits_vary; + TargetKconfig kconfig; }; typedef struct TargetInfoNode TargetInfoNode; diff --git a/include/qemu/target-kconfig-has.h b/include/qemu/target-kconfig-has.h new file mode 100644 index 00000000000..6616f75845a --- /dev/null +++ b/include/qemu/target-kconfig-has.h @@ -0,0 +1,7 @@ +/* + * QEMU target_has_kconfig_* helpers + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "target-kconfig-has-gen.h" diff --git a/include/qemu/target-kconfig.h b/include/qemu/target-kconfig.h new file mode 100644 index 00000000000..21b2ba576ec --- /dev/null +++ b/include/qemu/target-kconfig.h @@ -0,0 +1,34 @@ +/* + * QEMU target kconfig API + * + * Copyright (c) Yonggang Luo + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_KCONFIG_H +#define QEMU_TARGET_KCONFIG_H + +#include "qemu/compiler.h" + +/** + * struct TargetKconfig: + * + * Compile-time device and accel CONFIG_* bits. Not Kconfig.host + * or target/Kconfig. Target identity is TargetInfo, not these + * bits. Not kvm_enabled() etc. Fields and + * target_has_kconfig_<name>() are generated by + * scripts/gen-target-kconfig.py. + */ +typedef struct TargetKconfig { +#include "target-kconfig-fields.h" +} QEMU_PACKED TargetKconfig; + +/** + * target_kconfig: + * + * Returns: The current TargetKconfig (target_info()->kconfig). + */ +const TargetKconfig *target_kconfig(void); + +#endif diff --git a/meson.build b/meson.build index 93237429713..9485f4e97be 100644 --- a/meson.build +++ b/meson.build @@ -3444,6 +3444,19 @@ genh += custom_target('config-poison.h', capture: true, command: [find_program('scripts/make-config-poison.sh'), target_configs_h]) +genh += custom_target('target-kconfig', + output: ['target-kconfig-fields.h', + 'target-kconfig-init.h', + 'target-kconfig-has-gen.h', + 'target-kconfig-has.c.inc'], + depfile: 'target-kconfig.d', + command: [python, files('scripts/gen-target-kconfig.py'), + '--kconfig', meson.current_source_dir() / 'Kconfig', + '--depfile', '@DEPFILE@', + '--fields', '@OUTPUT0@', + '--init', '@OUTPUT1@', + '--has', '@OUTPUT2@', + '--has-c', '@OUTPUT3@']) if fdt_required.length() > 0 error('fdt disabled but required by targets ' + ', '.join(fdt_required)) @@ -3778,6 +3791,7 @@ endif qom_ss.add(files( 'target-info.c', + 'target-kconfig.c', )) qom_ss = qom_ss.apply({}) libqom = static_library('qom', qom_ss.sources() + genh, diff --git a/scripts/gen-target-kconfig.py b/scripts/gen-target-kconfig.py new file mode 100644 index 00000000000..afeffc75ca0 --- /dev/null +++ b/scripts/gen-target-kconfig.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +"""Generate TargetKconfig CONFIG_* field, initializer, and has headers. + +The field list is every `config FOO` from the QEMU Kconfig tree, +parsed with scripts/minikconf.py (including accel/Kconfig: KVM, +XEN, HVF, TCG, ...). Host proxies in Kconfig.host and the +target/Kconfig tree (arch tokens and TARGET_BIG_ENDIAN) are +omitted. Target identity is TargetInfo, not TargetKconfig. + +Each field also gets target_has_kconfig_<name>(const TargetInfo *). +""" + +import argparse +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from minikconf import KconfigData, KconfigParser + + +def field_name(config: str) -> str: + return config[len("CONFIG_"):].lower() + + +def collect_from_kconfig(root: str) -> tuple[set[str], list[str]]: + data = KconfigData() + with open(root, "rt", encoding="utf-8") as fp: + KconfigParser.parse(fp, data) + names = {"CONFIG_" + name for name in data.defined_vars} + return names, list(data.previously_included) + + +def collect_kconfig_names(path: str) -> set[str]: + data = KconfigData() + with open(path, "rt", encoding="utf-8") as fp: + KconfigParser.parse(fp, data) + return {"CONFIG_" + name for name in data.defined_vars} + + +def collect_host_kconfig_names(kconfig_root: str) -> set[str]: + host = os.path.join(os.path.dirname(os.path.abspath(kconfig_root)), + "Kconfig.host") + return collect_kconfig_names(host) + + +def collect_target_kconfig_names(kconfig_root: str) -> set[str]: + target = os.path.join(os.path.dirname(os.path.abspath(kconfig_root)), + "target", "Kconfig") + return collect_kconfig_names(target) + + +def write_fields(path: str, configs: list[str]) -> None: + lines = [ + "/* Generated by scripts/gen-target-kconfig.py. Do not edit. */", + "", + ] + for name in configs: + lines.append(f" uint32_t {field_name(name)} : 1;") + lines.append("") + with open(path, "w", encoding="utf-8", newline="\n") as fh: + fh.write("\n".join(lines)) + + +def write_init(path: str, configs: list[str]) -> None: + lines = [ + "/* Generated by scripts/gen-target-kconfig.py. Do not edit. */", + "", + ] + for name in configs: + field = field_name(name) + lines.append(f"#if defined({name})") + lines.append(f" .{field} = true,") + lines.append("#endif") + lines.append("") + with open(path, "w", encoding="utf-8", newline="\n") as fh: + fh.write("\n".join(lines)) + + +def has_fn(config: str) -> str: + return "target_has_kconfig_" + field_name(config) + + +def write_has_h(path: str, configs: list[str]) -> None: + lines = [ + "/* Generated by scripts/gen-target-kconfig.py. Do not edit. */", + "", + "#ifndef TARGET_KCONFIG_HAS_GEN_H", + "#define TARGET_KCONFIG_HAS_GEN_H", + "", + "#include <stdbool.h>", + "", + "typedef struct TargetInfo TargetInfo;", + "", + ] + for name in configs: + lines.append(f"bool {has_fn(name)}(const TargetInfo *ti);") + lines.append("") + lines.append("#endif") + lines.append("") + with open(path, "w", encoding="utf-8", newline="\n") as fh: + fh.write("\n".join(lines)) + + +def write_has_c(path: str, configs: list[str]) -> None: + lines = [ + "/* Generated by scripts/gen-target-kconfig.py. Do not edit. */", + "", + ] + for name in configs: + fn = has_fn(name) + field = field_name(name) + lines.append(f"bool {fn}(const TargetInfo *ti)") + lines.append("{") + lines.append(f" return ti->kconfig.{field};") + lines.append("}") + lines.append("") + with open(path, "w", encoding="utf-8", newline="\n") as fh: + fh.write("\n".join(lines)) + + +def write_depfile(path: str, outputs: list[str], deps: list[str]) -> None: + lines = [" ".join(outputs) + ":" ] + for dep in deps: + lines.append(f" {dep} \\") + if len(lines) > 1: + lines[-1] = lines[-1][:-2] + lines.append("") + with open(path, "w", encoding="utf-8", newline="\n") as fh: + fh.write("\n".join(lines)) + + +def main(argv: list[str]) -> int: + parser = argparse.ArgumentParser( + description="Generate TargetKconfig CONFIG_* include headers" + ) + parser.add_argument("--fields", required=True, help="output fields header") + parser.add_argument("--init", required=True, help="output initializer header") + parser.add_argument("--has", required=True, help="output has-function header") + parser.add_argument("--has-c", required=True, help="output has-function bodies") + parser.add_argument("--kconfig", required=True, help="root Kconfig") + parser.add_argument("--depfile", help="ninja depfile of walked Kconfig files") + args = parser.parse_args(argv) + + names, kconfig_files = collect_from_kconfig(args.kconfig) + names -= collect_host_kconfig_names(args.kconfig) + names -= collect_target_kconfig_names(args.kconfig) + configs = sorted(names) + + write_fields(args.fields, configs) + write_init(args.init, configs) + write_has_h(args.has, configs) + write_has_c(args.has_c, configs) + if args.depfile: + write_depfile(args.depfile, + [args.fields, args.init, args.has, args.has_c], + kconfig_files) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/target-info-def.c b/target-info-def.c index 3041d64ccd2..c02d98e6875 100644 --- a/target-info-def.c +++ b/target-info-def.c @@ -7,6 +7,9 @@ */ #include "qemu/osdep.h" +#ifdef CONFIG_DEVICES +#include CONFIG_DEVICES +#endif #include "qemu/target-info.h" #include "qemu/target-info-def.h" #include "hw/core/boards.h" @@ -38,6 +41,9 @@ static const TargetInfo target_info_stub = { .page_bits_vary = false, .page_bits_init = TARGET_PAGE_BITS, #endif + .kconfig = { +#include "target-kconfig-init.h" + }, }; target_info_init(target_info_stub) diff --git a/target-kconfig.c b/target-kconfig.c new file mode 100644 index 00000000000..078c0d8e7f3 --- /dev/null +++ b/target-kconfig.c @@ -0,0 +1,20 @@ +/* + * QEMU target kconfig helpers + * + * Copyright (c) Yonggang Luo + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-info.h" +#include "qemu/target-info-def.h" +#include "qemu/target-kconfig.h" +#include "qemu/target-kconfig-has.h" + +const TargetKconfig *target_kconfig(void) +{ + return &target_info()->kconfig; +} + +#include "target-kconfig-has.c.inc" diff --git a/tests/unit/meson.build b/tests/unit/meson.build index dc980a35955..c806dbc4ffa 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -47,6 +47,7 @@ tests = { objects: target_info_def_objects.values(), compile_args: ['-DTARGET_INFO_DEF_COUNT=@0@'.format( target_info_def_objects.values().length())])], + 'test-target-kconfig-has': [qom], 'test-qemu-opts': [], 'test-keyval': [testqapi], 'test-logging': [], diff --git a/tests/unit/test-target-kconfig-has.c b/tests/unit/test-target-kconfig-has.c new file mode 100644 index 00000000000..289ed89b854 --- /dev/null +++ b/tests/unit/test-target-kconfig-has.c @@ -0,0 +1,22 @@ +/* + * target-kconfig-has.h include path test + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-kconfig-has.h" + +static void test_include(void) +{ + bool (*fn)(const TargetInfo *ti) = target_has_kconfig_tcg; + + g_assert_nonnull(fn); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/target-kconfig-has/include", test_include); + return g_test_run(); +} -- 2.52.0.windows.1
