diff options
| -rw-r--r-- | .gitlab-ci.d/containers.yml | 7 | ||||
| -rw-r--r-- | .gitlab-ci.d/crossbuilds.yml | 46 | ||||
| -rw-r--r-- | .gitlab-ci.yml | 81 | ||||
| -rw-r--r-- | .travis.yml | 4 | ||||
| -rw-r--r-- | backends/hostmem-file.c | 13 | ||||
| -rwxr-xr-x | scripts/checkpatch.pl | 2 | ||||
| -rwxr-xr-x | scripts/device-crash-test | 96 | ||||
| -rw-r--r-- | scripts/qapi/commands.py | 3 | ||||
| -rw-r--r-- | scripts/qapi/events.py | 4 | ||||
| -rw-r--r-- | scripts/qapi/gen.py | 12 | ||||
| -rw-r--r-- | scripts/qapi/introspect.py | 349 | ||||
| -rw-r--r-- | scripts/qapi/mypy.ini | 5 | ||||
| -rw-r--r-- | scripts/qapi/schema.py | 2 | ||||
| -rw-r--r-- | scripts/qapi/types.py | 12 | ||||
| -rw-r--r-- | scripts/qapi/visit.py | 10 | ||||
| -rw-r--r-- | target/i386/cpu.c | 107 | ||||
| -rw-r--r-- | target/i386/cpu.h | 4 | ||||
| -rw-r--r-- | tests/qtest/boot-sector.c | 9 | ||||
| -rw-r--r-- | tests/qtest/boot-serial-test.c | 2 |
19 files changed, 558 insertions, 210 deletions
diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index 90fac85ce4..33e4046e23 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -20,13 +20,6 @@ - docker push "$TAG" after_script: - docker logout - rules: - - changes: - - .gitlab-ci.d/containers.yml - - tests/docker/* - - tests/docker/dockerfiles/* - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - if: '$CI_COMMIT_REF_NAME == "testing/next"' amd64-alpine-container: <<: *container_job_definition diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml index 66547b6683..d5098c986b 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -41,114 +41,158 @@ cross-armel-system: extends: .cross_system_build_job + needs: + job: armel-debian-cross-container variables: IMAGE: debian-armel-cross cross-armel-user: extends: .cross_user_build_job + needs: + job: armel-debian-cross-container variables: IMAGE: debian-armel-cross cross-armhf-system: extends: .cross_system_build_job + needs: + job: armhf-debian-cross-container variables: IMAGE: debian-armhf-cross cross-armhf-user: extends: .cross_user_build_job + needs: + job: armhf-debian-cross-container variables: IMAGE: debian-armhf-cross cross-arm64-system: extends: .cross_system_build_job + needs: + job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross cross-arm64-user: extends: .cross_user_build_job + needs: + job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross cross-i386-system: extends: .cross_system_build_job + needs: + job: i386-fedora-cross-container variables: IMAGE: fedora-i386-cross MAKE_CHECK_ARGS: check-qtest cross-i386-user: extends: .cross_user_build_job + needs: + job: i386-fedora-cross-container variables: IMAGE: fedora-i386-cross MAKE_CHECK_ARGS: check cross-mips-system: extends: .cross_system_build_job + needs: + job: mips-debian-cross-container variables: IMAGE: debian-mips-cross cross-mips-user: extends: .cross_user_build_job + needs: + job: mips-debian-cross-container variables: IMAGE: debian-mips-cross cross-mipsel-system: extends: .cross_system_build_job + needs: + job: mipsel-debian-cross-container variables: IMAGE: debian-mipsel-cross cross-mipsel-user: extends: .cross_user_build_job + needs: + job: mipsel-debian-cross-container variables: IMAGE: debian-mipsel-cross cross-mips64el-system: extends: .cross_system_build_job + needs: + job: mips64el-debian-cross-container variables: IMAGE: debian-mips64el-cross cross-mips64el-user: extends: .cross_user_build_job + needs: + job: mips64el-debian-cross-container variables: IMAGE: debian-mips64el-cross cross-ppc64el-system: extends: .cross_system_build_job + needs: + job: ppc64el-debian-cross-container variables: IMAGE: debian-ppc64el-cross cross-ppc64el-user: extends: .cross_user_build_job + needs: + job: ppc64el-debian-cross-container variables: IMAGE: debian-ppc64el-cross cross-s390x-system: extends: .cross_system_build_job + needs: + job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross cross-s390x-user: extends: .cross_user_build_job + needs: + job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross cross-s390x-kvm-only: extends: .cross_accel_build_job + needs: + job: s390x-debian-cross-container variables: IMAGE: debian-s390x-cross ACCEL_CONFIGURE_OPTS: --disable-tcg cross-win32-system: extends: .cross_system_build_job + needs: + job: win32-fedora-cross-container variables: IMAGE: fedora-win32-cross cross-win64-system: extends: .cross_system_build_job + needs: + job: win64-fedora-cross-container variables: IMAGE: fedora-win64-cross cross-amd64-xen-only: extends: .cross_accel_build_job + needs: + job: amd64-debian-cross-container variables: IMAGE: debian-amd64-cross ACCEL: xen @@ -156,6 +200,8 @@ cross-amd64-xen-only: cross-arm64-xen-only: extends: .cross_accel_build_job + needs: + job: arm64-debian-cross-container variables: IMAGE: debian-arm64-cross ACCEL: xen diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7adb9a4cef..8b6d495288 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,7 +64,7 @@ include: - echo "[datadir.paths]" > ~/.config/avocado/avocado.conf - echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']" >> ~/.config/avocado/avocado.conf - - echo -e '[job.output.testlogs]\nstatuses = ["FAIL"]' + - echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]' >> ~/.config/avocado/avocado.conf - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then du -chs ${CI_PROJECT_DIR}/avocado-cache ; @@ -109,6 +109,8 @@ acceptance-system-alpine: build-system-ubuntu: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --enable-fdt=system --enable-slirp=system @@ -141,6 +143,8 @@ acceptance-system-ubuntu: build-system-debian: <<: *native_build_job_definition + needs: + job: amd64-debian-container variables: IMAGE: debian-amd64 CONFIGURE_ARGS: --enable-fdt=system @@ -186,6 +190,8 @@ acceptance-system-debian: build-system-fedora: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs @@ -219,6 +225,8 @@ acceptance-system-fedora: build-system-centos: <<: *native_build_job_definition + needs: + job: amd64-centos8-container variables: IMAGE: centos8 CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system @@ -252,6 +260,8 @@ acceptance-system-centos: build-system-opensuse: <<: *native_build_job_definition + needs: + job: amd64-opensuse-leap-container variables: IMAGE: opensuse-leap CONFIGURE_ARGS: --enable-fdt=system @@ -272,18 +282,20 @@ check-system-opensuse: MAKE_CHECK_ARGS: check acceptance-system-opensuse: - <<: *native_test_job_definition - needs: - - job: build-system-opensuse - artifacts: true - variables: - IMAGE: opensuse-leap - MAKE_CHECK_ARGS: check-acceptance - <<: *acceptance_definition + <<: *native_test_job_definition + needs: + - job: build-system-opensuse + artifacts: true + variables: + IMAGE: opensuse-leap + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition build-disabled: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: @@ -346,6 +358,7 @@ build-disabled: --disable-vhost-crypto --disable-vhost-net --disable-vhost-scsi + --disable-vhost-kernel --disable-vhost-user --disable-vhost-vdpa --disable-vhost-vsock @@ -366,6 +379,8 @@ build-disabled: # available. build-tcg-disabled: <<: *native_build_job_definition + needs: + job: amd64-centos8-container variables: IMAGE: centos8 script: @@ -386,6 +401,8 @@ build-tcg-disabled: build-user: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system @@ -393,6 +410,8 @@ build-user: build-user-static: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --static @@ -401,6 +420,8 @@ build-user-static: # Only build the softmmu targets we have check-tcg tests for build-some-softmmu: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --enable-debug @@ -412,6 +433,8 @@ build-some-softmmu: # we skip cris-linux-user as it doesn't use the common run loop build-user-plugins: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user @@ -427,6 +450,8 @@ build-user-centos7: build-some-softmmu-plugins: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-user --enable-plugins --enable-debug-tcg @@ -435,6 +460,8 @@ build-some-softmmu-plugins: clang-system: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ @@ -464,6 +491,8 @@ tsan-build: # These targets are on the way out build-deprecated: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-docs --disable-tools @@ -501,6 +530,8 @@ gprof-gcov: build-oss-fuzz: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora script: @@ -519,8 +550,10 @@ build-oss-fuzz: build-tci: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: - IMAGE: fedora + IMAGE: debian-all-test-cross script: - TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64" - mkdir build @@ -528,7 +561,6 @@ build-tci: - ../configure --enable-tcg-interpreter --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" - - make run-tcg-tests-x86_64-softmmu - make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - for tg in $TARGETS ; do export QTEST_QEMU_BINARY="./qemu-system-${tg}" ; @@ -537,11 +569,14 @@ build-tci: done - QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test - QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow + - make check-tcg # Alternate coroutines implementations are only really of interest to KVM users # However we can't test against KVM on Gitlab-CI so we can only run unit tests build-coroutine-ucontext: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --with-coroutine=ucontext --disable-tcg @@ -549,6 +584,8 @@ build-coroutine-ucontext: build-coroutine-sigaltstack: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg @@ -560,6 +597,8 @@ build-coroutine-sigaltstack: # which had some API differences. build-crypto-old-nettle: <<: *native_build_job_definition + needs: + job: amd64-centos7-container variables: IMAGE: centos7 TARGETS: x86_64-softmmu x86_64-linux-user @@ -581,6 +620,8 @@ check-crypto-old-nettle: build-crypto-old-gcrypt: <<: *native_build_job_definition + needs: + job: amd64-centos7-container variables: IMAGE: centos7 TARGETS: x86_64-softmmu x86_64-linux-user @@ -602,6 +643,8 @@ check-crypto-old-gcrypt: build-crypto-only-gnutls: <<: *native_build_job_definition + needs: + job: amd64-centos7-container variables: IMAGE: centos7 TARGETS: x86_64-softmmu x86_64-linux-user @@ -623,18 +666,24 @@ check-crypto-only-gnutls: # We don't need to exercise every backend with every front-end build-trace-multi-user: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --enable-trace-backends=log,simple,syslog --disable-system build-trace-ftrace-system: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --enable-trace-backends=ftrace --target-list=x86_64-softmmu build-trace-ust-system: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 CONFIGURE_ARGS: --enable-trace-backends=ust --target-list=x86_64-softmmu @@ -642,12 +691,16 @@ build-trace-ust-system: # Check our reduced build configurations build-without-default-devices: <<: *native_build_job_definition + needs: + job: amd64-centos8-container variables: IMAGE: centos8 CONFIGURE_ARGS: --without-default-devices --disable-user build-without-default-features: <<: *native_build_job_definition + needs: + job: amd64-debian-container variables: IMAGE: debian-amd64 CONFIGURE_ARGS: --without-default-features --disable-user @@ -657,6 +710,8 @@ build-without-default-features: check-patch: stage: build image: $CI_REGISTRY_IMAGE/qemu/centos8:latest + needs: + job: amd64-centos8-container script: .gitlab-ci.d/check-patch.py except: variables: @@ -668,6 +723,8 @@ check-patch: check-dco: stage: build image: $CI_REGISTRY_IMAGE/qemu/centos8:latest + needs: + job: amd64-centos8-container script: .gitlab-ci.d/check-dco.py except: variables: @@ -678,6 +735,8 @@ check-dco: build-libvhost-user: stage: build image: $CI_REGISTRY_IMAGE/qemu/fedora:latest + needs: + job: amd64-fedora-container before_script: - dnf install -y meson ninja-build script: diff --git a/.travis.yml b/.travis.yml index fc27fd6330..4609240b5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,9 +86,11 @@ git: submodules: false # Common first phase for all steps +# We no longer use nproc to calculate jobs: +# https://travis-ci.community/t/nproc-reports-32-cores-on-arm64/5851 before_install: - if command -v ccache ; then ccache --zero-stats ; fi - - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - export JOBS=3 - echo "=== Using ${JOBS} simultaneous jobs ===" # Configure step - may be overridden diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 733408e076..b683da9daf 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -124,6 +124,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, fb->align = val; } +#ifdef CONFIG_LIBPMEM static bool file_memory_backend_get_pmem(Object *o, Error **errp) { return MEMORY_BACKEND_FILE(o)->is_pmem; @@ -140,17 +141,9 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) return; } -#ifndef CONFIG_LIBPMEM - if (value) { - error_setg(errp, "Lack of libpmem support while setting the 'pmem=on'" - " of %s. We can't ensure data persistence.", - object_get_typename(o)); - return; - } -#endif - fb->is_pmem = value; } +#endif /* CONFIG_LIBPMEM */ static bool file_memory_backend_get_readonly(Object *obj, Error **errp) { @@ -203,8 +196,10 @@ file_backend_class_init(ObjectClass *oc, void *data) file_memory_backend_get_align, file_memory_backend_set_align, NULL, NULL); +#ifdef CONFIG_LIBPMEM object_class_property_add_bool(oc, "pmem", file_memory_backend_get_pmem, file_memory_backend_set_pmem); +#endif object_class_property_add_bool(oc, "readonly", file_memory_backend_get_readonly, file_memory_backend_set_readonly); diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index e47ad878d8..7f194c842b 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1499,7 +1499,7 @@ sub process { $is_patch = 1; } - if ($line =~ /^Author: .*via Qemu-devel.*<qemu-devel\@nongnu.org>/) { + if ($line =~ /^(Author|From): .* via .*<qemu-devel\@nongnu.org>/) { ERROR("Author email address is mangled by the mailing list\n" . $herecurr); } diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 04118669ba..ef1412ca59 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -41,18 +41,18 @@ logger = logging.getLogger('device-crash-test') dbg = logger.debug -# Purposes of the following whitelist: +# Purposes of the following rule list: # * Avoiding verbose log messages when we find known non-fatal # (exitcode=1) errors # * Avoiding fatal errors when we find known crashes # * Skipping machines/devices that are known not to work out of # the box, when running in --quick mode # -# Keeping the whitelist updated is desirable, but not required, +# Keeping the rule list updated is desirable, but not required, # because unexpected cases where QEMU exits with exitcode=1 will # just trigger a INFO message. -# Valid whitelist entry keys: +# Valid error rule keys: # * accel: regexp, full match only # * machine: regexp, full match only # * device: regexp, full match only @@ -62,7 +62,7 @@ dbg = logger.debug # * expected: if True, QEMU is expected to always fail every time # when testing the corresponding test case # * loglevel: log level of log output when there's a match. -ERROR_WHITELIST = [ +ERROR_RULE_LIST = [ # Machines that won't work out of the box: # MACHINE | ERROR MESSAGE {'machine':'niagara', 'expected':True}, # Unable to load a firmware for -M niagara @@ -186,65 +186,65 @@ ERROR_WHITELIST = [ ] -def whitelistTestCaseMatch(wl, t): - """Check if a test case specification can match a whitelist entry +def errorRuleTestCaseMatch(rule, t): + """Check if a test case specification can match a error rule - This only checks if a whitelist entry is a candidate match + This only checks if a error rule is a candidate match for a given test case, it won't check if the test case - results/output match the entry. See whitelistResultMatch(). + results/output match the rule. See ruleListResultMatch(). """ - return (('machine' not in wl or + return (('machine' not in rule or 'machine' not in t or - re.match(wl['machine'] + '$', t['machine'])) and - ('accel' not in wl or + re.match(rule['machine'] + '$', t['machine'])) and + ('accel' not in rule or 'accel' not in t or - re.match(wl['accel'] + '$', t['accel'])) and - ('device' not in wl or + re.match(rule['accel'] + '$', t['accel'])) and + ('device' not in rule or 'device' not in t or - re.match(wl['device'] + '$', t['device']))) + re.match(rule['device'] + '$', t['device']))) -def whitelistCandidates(t): +def ruleListCandidates(t): """Generate the list of candidates that can match a test case""" - for i, wl in enumerate(ERROR_WHITELIST): - if whitelistTestCaseMatch(wl, t): - yield (i, wl) + for i, rule in enumerate(ERROR_RULE_LIST): + if errorRuleTestCaseMatch(rule, t): + yield (i, rule) def findExpectedResult(t): - """Check if there's an expected=True whitelist entry for a test case + """Check if there's an expected=True error rule for a test case - Returns (i, wl) tuple, where i is the index in - ERROR_WHITELIST and wl is the whitelist entry itself. + Returns (i, rule) tuple, where i is the index in + ERROR_RULE_LIST and rule is the error rule itself. """ - for i, wl in whitelistCandidates(t): - if wl.get('expected'): - return (i, wl) + for i, rule in ruleListCandidates(t): + if rule.get('expected'): + return (i, rule) -def whitelistResultMatch(wl, r): - """Check if test case results/output match a whitelist entry +def ruleListResultMatch(rule, r): + """Check if test case results/output match a error rule It is valid to call this function only if - whitelistTestCaseMatch() is True for the entry (e.g. on - entries returned by whitelistCandidates()) + errorRuleTestCaseMatch() is True for the rule (e.g. on + rules returned by ruleListCandidates()) """ - assert whitelistTestCaseMatch(wl, r['testcase']) - return ((wl.get('exitcode', 1) is None or - r['exitcode'] == wl.get('exitcode', 1)) and - ('log' not in wl or - re.search(wl['log'], r['log'], re.MULTILINE))) + assert errorRuleTestCaseMatch(rule, r['testcase']) + return ((rule.get('exitcode', 1) is None or + r['exitcode'] == rule.get('exitcode', 1)) and + ('log' not in rule or + re.search(rule['log'], r['log'], re.MULTILINE))) -def checkResultWhitelist(r): - """Look up whitelist entry for a given test case result +def checkResultRuleList(r): + """Look up error rule for a given test case result - Returns (i, wl) tuple, where i is the index in - ERROR_WHITELIST and wl is the whitelist entry itself. + Returns (i, rule) tuple, where i is the index in + ERROR_RULE_LIST and rule is the error rule itself. """ - for i, wl in whitelistCandidates(r['testcase']): - if whitelistResultMatch(wl, r): - return i, wl + for i, rule in ruleListCandidates(r['testcase']): + if ruleListResultMatch(rule, r): + return i, rule raise Exception("this should never happen") @@ -543,12 +543,12 @@ def main(): break if f: - i, wl = checkResultWhitelist(f) - dbg("testcase: %r, whitelist match: %r", t, wl) + i, rule = checkResultRuleList(f) + dbg("testcase: %r, rule list match: %r", t, rule) wl_stats.setdefault(i, []).append(f) - level = wl.get('loglevel', logging.DEBUG) + level = rule.get('loglevel', logging.DEBUG) logFailure(f, level) - if wl.get('fatal') or (args.strict and level >= logging.WARN): + if rule.get('fatal') or (args.strict and level >= logging.WARN): fatal_failures.append(f) else: dbg("success: %s", formatTestCase(t)) @@ -560,10 +560,10 @@ def main(): logger.info("Skipped %d test cases", skipped) if args.debug: - stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in - enumerate(ERROR_WHITELIST)], key=lambda x: x[0]) - for count, wl in stats: - dbg("whitelist entry stats: %d: %r", count, wl) + stats = sorted([(len(wl_stats.get(i, [])), rule) for i, rule in + enumerate(ERROR_RULE_LIST)], key=lambda x: x[0]) + for count, rule in stats: + dbg("error rule stats: %d: %r", count, rule) if fatal_failures: for f in fatal_failures: diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 54af519f44..0a75a9371b 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -17,6 +17,7 @@ from typing import ( Dict, List, Optional, + Sequence, Set, ) @@ -297,7 +298,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) def visit_command(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], ret_type: Optional[QAPISchemaType], diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 8c57deb2b8..90d2f6156d 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -12,7 +12,7 @@ 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 List, Optional +from typing import List, Optional, Sequence from .common import c_enum_const, c_name, mcgen from .gen import QAPISchemaModularCVisitor, build_params, ifcontext @@ -214,7 +214,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); def visit_event(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], arg_type: Optional[QAPISchemaObjectType], boxed: bool) -> None: diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 63549cc8d4..1fa503bdbd 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -17,8 +17,8 @@ import re from typing import ( Dict, Iterator, - List, Optional, + Sequence, Tuple, ) @@ -85,7 +85,7 @@ class QAPIGen: fp.write(text) -def _wrap_ifcond(ifcond: List[str], before: str, after: str) -> str: +def _wrap_ifcond(ifcond: Sequence[str], before: str, after: str) -> str: if before == after: return after # suppress empty #if ... #endif @@ -127,9 +127,9 @@ def build_params(arg_type: Optional[QAPISchemaObjectType], class QAPIGenCCode(QAPIGen): def __init__(self, fname: str): super().__init__(fname) - self._start_if: Optional[Tuple[List[str], str, str]] = None + self._start_if: Optional[Tuple[Sequence[str], str, str]] = None - def start_if(self, ifcond: List[str]) -> None: + def start_if(self, ifcond: Sequence[str]) -> None: assert self._start_if is None self._start_if = (ifcond, self._body, self._preamble) @@ -187,11 +187,11 @@ class QAPIGenH(QAPIGenC): @contextmanager -def ifcontext(ifcond: List[str], *args: QAPIGenCCode) -> Iterator[None]: +def ifcontext(ifcond: Sequence[str], *args: QAPIGenCCode) -> Iterator[None]: """ A with-statement context manager that wraps with `start_if()` / `end_if()`. - :param ifcond: A list of conditionals, passed to `start_if()`. + :param ifcond: A sequence of conditionals, passed to `start_if()`. :param args: any number of `QAPIGenCCode`. Example:: diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index fafec94e02..9a348ca2e5 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -1,15 +1,29 @@ """ QAPI introspection generator -Copyright (C) 2015-2018 Red Hat, Inc. +Copyright (C) 2015-2021 Red Hat, Inc. Authors: Markus Armbruster <armbru@redhat.com> + John Snow <jsnow@redhat.com> 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 ( + Any, + Dict, + Generic, + Iterable, + List, + Optional, + Sequence, + Tuple, + TypeVar, + Union, +) + from .common import ( c_name, gen_endif, @@ -18,91 +32,158 @@ from .common import ( ) from .gen import QAPISchemaMonolithicCVisitor from .schema import ( + QAPISchema, QAPISchemaArrayType, QAPISchemaBuiltinType, + QAPISchemaEntity, + QAPISchemaEnumMember, + QAPISchemaFeature, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, QAPISchemaType, + QAPISchemaVariant, + QAPISchemaVariants, ) - - -def _make_tree(obj, ifcond, features, extra=None): - if extra is None: - extra = {} - if ifcond: - extra['if'] = ifcond - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - if extra: - return (obj, extra) - return obj - - -def _tree_to_qlit(obj, level=0, suppress_first_indent=False): - - def indent(level): +from .source import QAPISourceInfo + + +# This module constructs a tree data structure that is used to +# generate the introspection information for QEMU. It is shaped +# like a JSON value. +# +# A complexity over JSON is that our values may or may not be annotated. +# +# Un-annotated values may be: +# Scalar: str, bool, None. +# Non-scalar: List, Dict +# _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]] +# +# With optional annotations, the type of all values is: +# JSONValue = Union[_Value, Annotated[_Value]] +# +# Sadly, mypy does not support recursive types; so the _Stub alias is used to +# mark the imprecision in the type model where we'd otherwise use JSONValue. +_Stub = Any +_Scalar = Union[str, bool, None] +_NonScalar = Union[Dict[str, _Stub], List[_Stub]] +_Value = Union[_Scalar, _NonScalar] +JSONValue = Union[_Value, 'Annotated[_Value]'] + +# These types are based on structures defined in QEMU's schema, so we +# lack precise types for them here. Python 3.6 does not offer +# TypedDict constructs, so they are broadly typed here as simple +# Python Dicts. +SchemaInfo = Dict[str, object] +SchemaInfoObject = Dict[str, object] +SchemaInfoObjectVariant = Dict[str, object] +SchemaInfoObjectMember = Dict[str, object] +SchemaInfoCommand = Dict[str, object] + + +_ValueT = TypeVar('_ValueT', bound=_Value) + + +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: Iterable[str], + comment: Optional[str] = None): + self.value = value + self.comment: Optional[str] = comment + self.ifcond: Tuple[str, ...] = tuple(ifcond) + + +def _tree_to_qlit(obj: JSONValue, + level: int = 0, + dict_value: bool = False) -> str: + """ + Convert the type tree into a QLIT C string, recursively. + + :param obj: The value to convert. + This value may not be Annotated when dict_value is True. + :param level: The indentation level for this particular value. + :param dict_value: True when the value being processed belongs to a + dict key; which suppresses the output indent. + """ + + def indent(level: int) -> str: return level * 4 * ' ' - if isinstance(obj, tuple): - ifobj, extra = obj - ifcond = extra.get('if') - comment = extra.get('comment') + if isinstance(obj, Annotated): + # NB: _tree_to_qlit is called recursively on the values of a + # key:value pair; those values can't be decorated with + # comments or conditionals. + msg = "dict values cannot have attached comments or if-conditionals." + assert not dict_value, msg + ret = '' - if comment: - ret += indent(level) + '/* %s */\n' % comment - if ifcond: - ret += gen_if(ifcond) - ret += _tree_to_qlit(ifobj, level) - if ifcond: - ret += '\n' + gen_endif(ifcond) + if obj.comment: + ret += indent(level) + f"/* {obj.comment} */\n" + if obj.ifcond: + ret += gen_if(obj.ifcond) + ret += _tree_to_qlit(obj.value, level) + if obj.ifcond: + ret += '\n' + gen_endif(obj.ifcond) return ret ret = '' - if not suppress_first_indent: + if not dict_value: ret += indent(level) + + # Scalars: if obj is None: ret += 'QLIT_QNULL' elif isinstance(obj, str): - ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' + ret += f"QLIT_QSTR({to_c_string(obj)})" + elif isinstance(obj, bool): + ret += f"QLIT_QBOOL({str(obj).lower()})" + + # Non-scalars: elif isinstance(obj, list): - elts = [_tree_to_qlit(elt, level + 1).strip('\n') - for elt in obj] - elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' - ret += '\n'.join(elts) + '\n' + for value in obj: + ret += _tree_to_qlit(value, level + 1).strip('\n') + '\n' + ret += indent(level + 1) + '{}\n' ret += indent(level) + '}))' elif isinstance(obj, dict): - elts = [] - for key, value in sorted(obj.items()): - elts.append(indent(level + 1) + '{ %s, %s }' % - (to_c_string(key), - _tree_to_qlit(value, level + 1, True))) - elts.append(indent(level + 1) + '{}') ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n' - ret += ',\n'.join(elts) + '\n' + for key, value in sorted(obj.items()): + ret += indent(level + 1) + "{{ {:s}, {:s} }},\n".format( + to_c_string(key), + _tree_to_qlit(value, level + 1, dict_value=True) + ) + ret += indent(level + 1) + '{}\n' ret += indent(level) + '}))' - elif isinstance(obj, bool): - ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false') else: - assert False # not implemented + raise NotImplementedError( + f"type '{type(obj).__name__}' not implemented" + ) + if level > 0: ret += ',' return ret -def to_c_string(string): +def to_c_string(string: str) -> str: return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): - def __init__(self, prefix, unmask): + def __init__(self, prefix: str, unmask: bool): super().__init__( prefix, 'qapi-introspect', ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask - self._schema = None - self._trees = [] - self._used_types = [] - self._name_map = {} + self._schema: Optional[QAPISchema] = None + self._trees: List[Annotated[SchemaInfo]] = [] + self._used_types: List[QAPISchemaType] = [] + self._name_map: Dict[str, str] = {} self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "%(prefix)sqapi-introspect.h" @@ -110,10 +191,10 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): ''', prefix=prefix)) - def visit_begin(self, schema): + def visit_begin(self, schema: QAPISchema) -> None: self._schema = schema - def visit_end(self): + def visit_end(self) -> None: # visit the types that are actually used for typ in self._used_types: typ.visit(self) @@ -135,18 +216,20 @@ const QLitObject %(c_name)s = %(c_string)s; self._used_types = [] self._name_map = {} - def visit_needed(self, entity): + def visit_needed(self, entity: QAPISchemaEntity) -> bool: # Ignore types on first pass; visit_end() will pick up used types return not isinstance(entity, QAPISchemaType) - def _name(self, name): + def _name(self, name: str) -> str: if self._unmask: return name if name not in self._name_map: self._name_map[name] = '%d' % len(self._name_map) return self._name_map[name] - def _use_type(self, typ): + def _use_type(self, typ: QAPISchemaType) -> str: + assert self._schema is not None + # Map the various integer types to plain int if typ.json_type() == 'int': typ = self._schema.lookup_type('int') @@ -165,81 +248,137 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_tree(self, name, mtype, obj, ifcond, features): - extra = None + @staticmethod + def _gen_features(features: Sequence[QAPISchemaFeature] + ) -> List[Annotated[str]]: + return [Annotated(f.name, f.ifcond) for f in features] + + def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object], + ifcond: Sequence[str] = (), + features: Sequence[QAPISchemaFeature] = ()) -> None: + """ + Build and append a SchemaInfo object to self._trees. + + :param name: The SchemaInfo's name. + :param mtype: The SchemaInfo's meta-type. + :param obj: Additional SchemaInfo members, as appropriate for + the meta-type. + :param ifcond: Conditionals to apply to the SchemaInfo. + :param features: The SchemaInfo's features. + Will be omitted from the output if empty. + """ + comment: Optional[str] = None if mtype not in ('command', 'event', 'builtin', 'array'): if not self._unmask: # Output a comment to make it easy to map masked names # back to the source when reading the generated output. - extra = {'comment': '"%s" = %s' % (self._name(name), name)} + comment = f'"{self._name(name)}" = {name}' name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._trees.append(_make_tree(obj, ifcond, features, extra)) - - def _gen_member(self, member): - obj = {'name': member.name, 'type': self._use_type(member.type)} + if features: + obj['features'] = self._gen_features(features) + self._trees.append(Annotated(obj, ifcond, comment)) + + def _gen_member(self, member: QAPISchemaObjectTypeMember + ) -> Annotated[SchemaInfoObjectMember]: + obj: SchemaInfoObjectMember = { + 'name': member.name, + 'type': self._use_type(member.type) + } if member.optional: obj['default'] = None - return _make_tree(obj, member.ifcond, member.features) - - def _gen_variants(self, tag_name, variants): - return {'tag': tag_name, - 'variants': [self._gen_variant(v) for v in variants]} - - def _gen_variant(self, variant): - obj = {'case': variant.name, 'type': self._use_type(variant.type)} - return _make_tree(obj, variant.ifcond, None) - - def visit_builtin_type(self, name, info, json_type): - self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None) - - def visit_enum_type(self, name, info, ifcond, features, members, prefix): - self._gen_tree(name, 'enum', - {'values': [_make_tree(m.name, m.ifcond, None) - for m in members]}, - ifcond, features) - - def visit_array_type(self, name, info, ifcond, element_type): + if member.features: + obj['features'] = self._gen_features(member.features) + return Annotated(obj, member.ifcond) + + def _gen_variant(self, variant: QAPISchemaVariant + ) -> Annotated[SchemaInfoObjectVariant]: + obj: SchemaInfoObjectVariant = { + 'case': variant.name, + 'type': self._use_type(variant.type) + } + return Annotated(obj, variant.ifcond) + + def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo], + json_type: str) -> None: + self._gen_tree(name, 'builtin', {'json-type': json_type}) + + def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str]) -> None: + self._gen_tree( + name, 'enum', + {'values': [Annotated(m.name, m.ifcond) for m in members]}, + ifcond, features + ) + + def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], + element_type: QAPISchemaType) -> None: element = self._use_type(element_type) self._gen_tree('[' + element + ']', 'array', {'element-type': element}, - ifcond, None) - - def visit_object_type_flat(self, name, info, ifcond, features, - members, variants): - obj = {'members': [self._gen_member(m) for m in members]} + ifcond) + + def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], + features: List[QAPISchemaFeature], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> None: + obj: SchemaInfoObject = { + 'members': [self._gen_member(m) for m in members] + } if variants: - obj.update(self._gen_variants(variants.tag_member.name, - variants.variants)) - + obj['tag'] = variants.tag_member.name + obj['variants'] = [self._gen_variant(v) for v in variants.variants] self._gen_tree(name, 'object', obj, ifcond, features) - def visit_alternate_type(self, name, info, ifcond, features, variants): - self._gen_tree(name, 'alternate', - {'members': [ - _make_tree({'type': self._use_type(m.type)}, - m.ifcond, None) - for m in variants.variants]}, - ifcond, features) + def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants) -> None: + self._gen_tree( + name, 'alternate', + {'members': [Annotated({'type': self._use_type(m.type)}, + m.ifcond) + for m in variants.variants]}, + ifcond, features + ) + + def visit_command(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + ret_type: Optional[QAPISchemaType], gen: bool, + success_response: bool, boxed: bool, allow_oob: bool, + allow_preconfig: bool, coroutine: bool) -> None: + assert self._schema is not None - def visit_command(self, name, info, ifcond, features, - arg_type, ret_type, gen, success_response, boxed, - allow_oob, allow_preconfig, coroutine): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type - obj = {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type)} + obj: SchemaInfoCommand = { + 'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type) + } if allow_oob: obj['allow-oob'] = allow_oob self._gen_tree(name, 'command', obj, ifcond, features) - def visit_event(self, name, info, ifcond, features, arg_type, boxed): + def visit_event(self, name: str, info: Optional[QAPISourceInfo], + ifcond: Sequence[str], features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + boxed: bool) -> None: + assert self._schema is not None + arg_type = arg_type or self._schema.the_empty_object_type self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)}, ifcond, features) -def gen_introspect(schema, output_dir, prefix, opt_unmask): +def gen_introspect(schema: QAPISchema, output_dir: str, prefix: str, + opt_unmask: bool) -> None: vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask) schema.visit(vis) vis.write(output_dir) diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 04bd5db527..0a000d58b3 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -13,11 +13,6 @@ disallow_untyped_defs = False disallow_incomplete_defs = False check_untyped_defs = False -[mypy-qapi.introspect] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False - [mypy-qapi.parser] disallow_untyped_defs = False disallow_incomplete_defs = False diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 353e8020a2..ff16578f6d 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -28,7 +28,7 @@ from .parser import QAPISchemaParser class QAPISchemaEntity: meta: Optional[str] = None - def __init__(self, name, info, doc, ifcond=None, features=None): + def __init__(self, name: str, info, doc, ifcond=None, features=None): assert name is None or isinstance(name, str) for f in features or []: assert isinstance(f, QAPISchemaFeature) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 2bdd626847..20d572a23a 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -13,7 +13,7 @@ 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 List, Optional +from typing import List, Optional, Sequence from .common import ( c_enum_const, @@ -139,7 +139,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: return ret -def gen_object(name: str, ifcond: List[str], +def gen_object(name: str, ifcond: Sequence[str], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], variants: Optional[QAPISchemaVariants]) -> str: @@ -307,7 +307,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], members: List[QAPISchemaEnumMember], prefix: Optional[str]) -> None: @@ -318,7 +318,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], element_type: QAPISchemaType) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_fwd_object_or_array(name)) @@ -328,7 +328,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_object_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], @@ -351,7 +351,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], variants: QAPISchemaVariants) -> None: with ifcontext(ifcond, self._genh): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 22e62df901..9aa0b1e11e 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -13,7 +13,7 @@ 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 List, Optional +from typing import List, Optional, Sequence from .common import ( c_enum_const, @@ -337,7 +337,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], members: List[QAPISchemaEnumMember], prefix: Optional[str]) -> None: @@ -348,7 +348,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_array_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], element_type: QAPISchemaType) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) @@ -357,7 +357,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_object_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], @@ -379,7 +379,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], - ifcond: List[str], + ifcond: Sequence[str], features: List[QAPISchemaFeature], variants: QAPISchemaVariants) -> None: with ifcontext(ifcond, self._genh, self._genc): diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 20c3a5af3f..6a53446e6a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1033,7 +1033,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "clzero", NULL, "xsaveerptr", NULL, NULL, NULL, NULL, NULL, NULL, "wbnoinvd", NULL, NULL, - "ibpb", NULL, NULL, "amd-stibp", + "ibpb", NULL, "ibrs", "amd-stibp", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, @@ -1798,6 +1798,56 @@ static CPUCaches epyc_rome_cache_info = { }, }; +static CPUCaches epyc_milan_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -4130,6 +4180,61 @@ static X86CPUDefinition builtin_x86_defs[] = { .model_id = "AMD EPYC-Rome Processor", .cache_info = &epyc_rome_cache_info, }, + { + .name = "EPYC-Milan", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 25, + .model = 1, + .stepping = 1, + .features[FEAT_1_EDX] = + CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | 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, + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | + CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_PCID, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR | + CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_IBPB | + CPUID_8000_0008_EBX_IBRS | CPUID_8000_0008_EBX_STIBP | + CPUID_8000_0008_EBX_AMD_SSBD, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | + CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_INVPCID, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_PKU, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM, + .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_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE | CPUID_SVM_SVME_ADDR_CHK, + .xlevel = 0x8000001E, + .model_id = "AMD EPYC-Milan Processor", + .cache_info = &epyc_milan_cache_info, + }, }; /* KVM-specific features that are automatically added/removed diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 82c1ac00ef..8be39cfb62 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -817,8 +817,12 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_8000_0008_EBX_WBNOINVD (1U << 9) /* Indirect Branch Prediction Barrier */ #define CPUID_8000_0008_EBX_IBPB (1U << 12) +/* Indirect Branch Restricted Speculation */ +#define CPUID_8000_0008_EBX_IBRS (1U << 14) /* Single Thread Indirect Branch Predictors */ #define CPUID_8000_0008_EBX_STIBP (1U << 15) +/* Speculative Store Bypass Disable */ +#define CPUID_8000_0008_EBX_AMD_SSBD (1U << 24) #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c index 24df5c4734..ea8f264661 100644 --- a/tests/qtest/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -138,6 +138,7 @@ void boot_sector_test(QTestState *qts) uint8_t signature_low; uint8_t signature_high; uint16_t signature; + QDict *qrsp, *qret; int i; /* Wait at most 600 seconds (test is slow with TCI and --enable-debug) */ @@ -155,6 +156,14 @@ void boot_sector_test(QTestState *qts) if (signature == SIGNATURE) { break; } + + /* check that guest is still in "running" state and did not panic */ + qrsp = qtest_qmp(qts, "{ 'execute': 'query-status' }"); + qret = qdict_get_qdict(qrsp, "return"); + g_assert_nonnull(qret); + g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + qobject_unref(qrsp); + g_usleep(TEST_DELAY); } diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index b6b1c23cd0..d74509b1c5 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -149,7 +149,7 @@ static testdef_t tests[] = { { "arm", "raspi2", "", "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" }, - { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), + { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, |