Ver código fonte

Merge tag 'for-upstream-i386' of https://gitlab.com/bonzini/qemu into staging

* target/i386: new feature bits for AMD processors
* target/i386/tcg: improvements around flag handling
* target/i386: add AVX10 support
* target/i386: add GraniteRapids-v2 model
* dockerfiles: add libcbor
* New nitro-enclave machine type
* qom: cleanups to object_new
* configure: detect 64-bit MIPS for rust
* configure: deprecate 32-bit MIPS

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmcjvkQUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroPIKgf/etNpO2T+eLFtWN/Qd5eopBXqNd9k
# KmeK9EgW9lqx2IPGNen33O+uKpb/TsMmubSsSF+YxTp7pmkc8+71f3rBMaIAD02r
# /paHSMVw0+f12DAFQz1jdvGihR7Mew0wcF/UdEt737y6vEmPxLTyYG3Gfa4NSZwT
# /V5jTOIcfUN/UEjNgIp6NTuOEESKmlqt22pfMapgkwMlAJYeeJU2X9eGYE86wJbq
# ZSXNgK3jL9wGT2XKa3e+OKzHfFpSkrB0JbQbdico9pefnBokN/hTeeUJ81wBAc7u
# i00W1CEQVJ5lhBc121d4AWMp83ME6HijJUOTMmJbFIONPsITFPHK1CAkng==
# =D4nR
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 31 Oct 2024 17:28:36 GMT
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream-i386' of https://gitlab.com/bonzini/qemu: (49 commits)
  target/i386: Introduce GraniteRapids-v2 model
  target/i386: Add AVX512 state when AVX10 is supported
  target/i386: Add feature dependencies for AVX10
  target/i386: add CPUID.24 features for AVX10
  target/i386: add AVX10 feature and AVX10 version property
  target/i386: return bool from x86_cpu_filter_features
  target/i386: do not rely on ExtSaveArea for accelerator-supported XCR0 bits
  target/i386: cpu: set correct supported XCR0 features for TCG
  target/i386: use + to put flags together
  target/i386: use higher-precision arithmetic to compute CF
  target/i386: use compiler builtin to compute PF
  target/i386: make flag variables unsigned
  target/i386: add a note about gen_jcc1
  target/i386: add a few more trivial CCPrepare cases
  target/i386: optimize TEST+Jxx sequences
  target/i386: optimize computation of ZF from CC_OP_DYNAMIC
  target/i386: Wrap cc_op_live with a validity check
  target/i386: Introduce cc_op_size
  target/i386: Rearrange CCOp
  target/i386: remove CC_OP_CLR
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 9 meses atrás
pai
commit
c94bee4cd6
84 arquivos alterados com 4218 adições e 349 exclusões
  1. 1 1
      .gitlab-ci.d/buildtest-template.yml
  2. 1 1
      .gitlab-ci.d/buildtest.yml
  3. 1 1
      .gitlab-ci.d/cirrus/freebsd-14.vars
  4. 1 1
      .gitlab-ci.d/cirrus/macos-14.vars
  5. 1 1
      .gitlab-ci.d/cirrus/macos-15.vars
  6. 6 0
      Kconfig.host
  7. 21 0
      MAINTAINERS
  8. 1 3
      accel/accel-system.c
  9. 0 2
      backends/hostmem-memfd.c
  10. 1 0
      configs/devices/i386-softmmu/default.mak
  11. 7 3
      configure
  12. 1 1
      docs/about/build-platforms.rst
  13. 8 4
      docs/about/deprecated.rst
  14. 78 0
      docs/system/i386/nitro-enclave.rst
  15. 2 1
      docs/system/target-i386.rst
  16. 1 0
      host/include/i386/host/cpuinfo.h
  17. 4 0
      hw/core/Kconfig
  18. 719 0
      hw/core/eif.c
  19. 22 0
      hw/core/eif.h
  20. 38 33
      hw/core/machine.c
  21. 1 0
      hw/core/meson.build
  22. 3 18
      hw/core/qdev.c
  23. 10 0
      hw/i386/Kconfig
  24. 1 0
      hw/i386/meson.build
  25. 5 1
      hw/i386/microvm.c
  26. 354 0
      hw/i386/nitro_enclave.c
  27. 4 0
      hw/virtio/Kconfig
  28. 321 0
      hw/virtio/cbor-helpers.c
  29. 2 0
      hw/virtio/meson.build
  30. 73 0
      hw/virtio/virtio-nsm-pci.c
  31. 1732 0
      hw/virtio/virtio-nsm.c
  32. 2 0
      include/hw/boards.h
  33. 2 0
      include/hw/i386/microvm.h
  34. 62 0
      include/hw/i386/nitro_enclave.h
  35. 45 0
      include/hw/virtio/cbor-helpers.h
  36. 49 0
      include/hw/virtio/virtio-nsm.h
  37. 9 0
      include/qemu/host-utils.h
  38. 0 8
      include/qom/object.h
  39. 2 0
      include/sysemu/hostmem.h
  40. 17 0
      meson.build
  41. 2 0
      meson_options.txt
  42. 36 44
      qom/object.c
  43. 2 2
      qom/object_interfaces.c
  44. 2 2
      qom/qom-qmp-cmds.c
  45. 3 0
      scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml
  46. 3 0
      scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml
  47. 0 0
      scripts/meson-buildoptions.
  48. 3 0
      scripts/meson-buildoptions.sh
  49. 6 1
      stubs/meson.build
  50. 11 7
      target/i386/cpu-dump.c
  51. 195 23
      target/i386/cpu.c
  52. 65 13
      target/i386/cpu.h
  53. 1 0
      target/i386/helper.h
  54. 3 13
      target/i386/host-cpu.c
  55. 31 21
      target/i386/hvf/x86_cpuid.c
  56. 0 4
      target/i386/kvm/kvm-cpu.c
  57. 2 1
      target/i386/kvm/kvm.c
  58. 13 38
      target/i386/tcg/cc_helper.c
  59. 82 45
      target/i386/tcg/cc_helper_template.h.inc
  60. 3 3
      target/i386/tcg/decode-new.c.inc
  61. 9 15
      target/i386/tcg/emit.c.inc
  62. 5 1
      target/i386/tcg/helper-tcg.h
  63. 2 2
      target/i386/tcg/int_helper.c
  64. 69 34
      target/i386/tcg/translate.c
  65. 3 0
      tests/docker/dockerfiles/alpine.docker
  66. 2 0
      tests/docker/dockerfiles/centos9.docker
  67. 4 0
      tests/docker/dockerfiles/debian-amd64-cross.docker
  68. 4 0
      tests/docker/dockerfiles/debian-arm64-cross.docker
  69. 4 0
      tests/docker/dockerfiles/debian-armhf-cross.docker
  70. 4 0
      tests/docker/dockerfiles/debian-i686-cross.docker
  71. 4 0
      tests/docker/dockerfiles/debian-mips64el-cross.docker
  72. 4 0
      tests/docker/dockerfiles/debian-mipsel-cross.docker
  73. 4 0
      tests/docker/dockerfiles/debian-ppc64el-cross.docker
  74. 4 0
      tests/docker/dockerfiles/debian-s390x-cross.docker
  75. 3 0
      tests/docker/dockerfiles/debian.docker
  76. 3 0
      tests/docker/dockerfiles/fedora-rust-nightly.docker
  77. 2 0
      tests/docker/dockerfiles/fedora-win64-cross.docker
  78. 3 0
      tests/docker/dockerfiles/fedora.docker
  79. 3 0
      tests/docker/dockerfiles/opensuse-leap.docker
  80. 3 0
      tests/docker/dockerfiles/ubuntu2204.docker
  81. 3 0
      tests/lcitool/projects/qemu.yml
  82. 2 1
      tests/qtest/libqtest.c
  83. 2 0
      tests/vm/generated/freebsd.json
  84. 1 0
      util/cpuinfo-i386.c

+ 1 - 1
.gitlab-ci.d/buildtest-template.yml

@@ -66,7 +66,7 @@
     - source scripts/ci/gitlab-ci-section
     - source scripts/ci/gitlab-ci-section
     - section_start buildenv "Setting up to run tests"
     - section_start buildenv "Setting up to run tests"
     - scripts/git-submodule.sh update roms/SLOF
     - scripts/git-submodule.sh update roms/SLOF
-    - meson subprojects download $(cd build/subprojects && echo *)
+    - build/pyvenv/bin/meson subprojects download $(cd build/subprojects && echo *)
     - cd build
     - cd build
     - find . -type f -exec touch {} +
     - find . -type f -exec touch {} +
     # Avoid recompiling by hiding ninja with NINJA=":"
     # Avoid recompiling by hiding ninja with NINJA=":"

+ 1 - 1
.gitlab-ci.d/buildtest.yml

@@ -115,7 +115,7 @@ build-system-fedora:
     job: amd64-fedora-container
     job: amd64-fedora-container
   variables:
   variables:
     IMAGE: fedora
     IMAGE: fedora
-    CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg
+    CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
     TARGETS: microblaze-softmmu mips-softmmu
     TARGETS: microblaze-softmmu mips-softmmu
       xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
       xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
     MAKE_CHECK_ARGS: check-build
     MAKE_CHECK_ARGS: check-build

+ 1 - 1
.gitlab-ci.d/cirrus/freebsd-14.vars

@@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake'
 NINJA='/usr/local/bin/ninja'
 NINJA='/usr/local/bin/ninja'
 PACKAGING_COMMAND='pkg'
 PACKAGING_COMMAND='pkg'
 PIP3='/usr/local/bin/pip-3.8'
 PIP3='/usr/local/bin/pip-3.8'
-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 sdl2 sdl2_image snappy sndio socat spice-protocol tesseract usbredir virglrenderer vte3 xorriso zstd'
+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'
 PYPI_PKGS=''
 PYPI_PKGS=''
 PYTHON='/usr/local/bin/python3'
 PYTHON='/usr/local/bin/python3'

+ 1 - 1
.gitlab-ci.d/cirrus/macos-14.vars

@@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake'
 NINJA='/opt/homebrew/bin/ninja'
 NINJA='/opt/homebrew/bin/ninja'
 PACKAGING_COMMAND='brew'
 PACKAGING_COMMAND='brew'
 PIP3='/opt/homebrew/bin/pip3'
 PIP3='/opt/homebrew/bin/pip3'
-PKGS='bash bc 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 libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio 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 xorriso zlib zstd'
 PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
 PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
 PYTHON='/opt/homebrew/bin/python3'
 PYTHON='/opt/homebrew/bin/python3'

+ 1 - 1
.gitlab-ci.d/cirrus/macos-15.vars

@@ -11,6 +11,6 @@ MAKE='/opt/homebrew/bin/gmake'
 NINJA='/opt/homebrew/bin/ninja'
 NINJA='/opt/homebrew/bin/ninja'
 PACKAGING_COMMAND='brew'
 PACKAGING_COMMAND='brew'
 PIP3='/opt/homebrew/bin/pip3'
 PIP3='/opt/homebrew/bin/pip3'
-PKGS='bash bc 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 libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio 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 xorriso zlib zstd'
 PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
 PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli'
 PYTHON='/opt/homebrew/bin/python3'
 PYTHON='/opt/homebrew/bin/python3'

+ 6 - 0
Kconfig.host

@@ -5,6 +5,12 @@
 config LINUX
 config LINUX
     bool
     bool
 
 
+config LIBCBOR
+    bool
+
+config GNUTLS
+    bool
+
 config OPENGL
 config OPENGL
     bool
     bool
 
 

+ 21 - 0
MAINTAINERS

@@ -132,6 +132,7 @@ F: configs/targets/mips*
 
 
 X86 general architecture support
 X86 general architecture support
 M: Paolo Bonzini <pbonzini@redhat.com>
 M: Paolo Bonzini <pbonzini@redhat.com>
+R: Zhao Liu <zhao1.liu@intel.com>
 S: Maintained
 S: Maintained
 F: configs/devices/i386-softmmu/default.mak
 F: configs/devices/i386-softmmu/default.mak
 F: configs/targets/i386-softmmu.mak
 F: configs/targets/i386-softmmu.mak
@@ -1839,6 +1840,16 @@ F: hw/i386/microvm.c
 F: include/hw/i386/microvm.h
 F: include/hw/i386/microvm.h
 F: pc-bios/bios-microvm.bin
 F: pc-bios/bios-microvm.bin
 
 
+nitro-enclave
+M: Alexander Graf <graf@amazon.com>
+M: Dorjoy Chowdhury <dorjoychy111@gmail.com>
+S: Maintained
+F: hw/core/eif.c
+F: hw/core/eif.h
+F: hw/i386/nitro_enclave.c
+F: include/hw/i386/nitro_enclave.h
+F: docs/system/i386/nitro-enclave.rst
+
 Machine core
 Machine core
 M: Eduardo Habkost <eduardo@habkost.net>
 M: Eduardo Habkost <eduardo@habkost.net>
 M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
 M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
@@ -2298,6 +2309,16 @@ F: include/sysemu/rng*.h
 F: backends/rng*.c
 F: backends/rng*.c
 F: tests/qtest/virtio-rng-test.c
 F: tests/qtest/virtio-rng-test.c
 
 
+virtio-nsm
+M: Alexander Graf <graf@amazon.com>
+M: Dorjoy Chowdhury <dorjoychy111@gmail.com>
+S: Maintained
+F: hw/virtio/cbor-helpers.c
+F: hw/virtio/virtio-nsm.c
+F: hw/virtio/virtio-nsm-pci.c
+F: include/hw/virtio/cbor-helpers.h
+F: include/hw/virtio/virtio-nsm.h
+
 vhost-user-stubs
 vhost-user-stubs
 M: Alex Bennée <alex.bennee@linaro.org>
 M: Alex Bennée <alex.bennee@linaro.org>
 S: Maintained
 S: Maintained

+ 1 - 3
accel/accel-system.c

@@ -73,19 +73,17 @@ void accel_system_init_ops_interfaces(AccelClass *ac)
     g_assert(ac_name != NULL);
     g_assert(ac_name != NULL);
 
 
     ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
     ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name);
-    ops = ACCEL_OPS_CLASS(module_object_class_by_name(ops_name));
     oc = module_object_class_by_name(ops_name);
     oc = module_object_class_by_name(ops_name);
     if (!oc) {
     if (!oc) {
         error_report("fatal: could not load module for type '%s'", ops_name);
         error_report("fatal: could not load module for type '%s'", ops_name);
         exit(1);
         exit(1);
     }
     }
     g_free(ops_name);
     g_free(ops_name);
-    ops = ACCEL_OPS_CLASS(oc);
     /*
     /*
      * all accelerators need to define ops, providing at least a mandatory
      * all accelerators need to define ops, providing at least a mandatory
      * non-NULL create_vcpu_thread operation.
      * non-NULL create_vcpu_thread operation.
      */
      */
-    g_assert(ops != NULL);
+    ops = ACCEL_OPS_CLASS(oc);
     if (ops->ops_init) {
     if (ops->ops_init) {
         ops->ops_init(ops);
         ops->ops_init(ops);
     }
     }

+ 0 - 2
backends/hostmem-memfd.c

@@ -18,8 +18,6 @@
 #include "qapi/error.h"
 #include "qapi/error.h"
 #include "qom/object.h"
 #include "qom/object.h"
 
 
-#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
-
 OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD)
 OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD)
 
 
 
 

+ 1 - 0
configs/devices/i386-softmmu/default.mak

@@ -29,3 +29,4 @@
 # CONFIG_I440FX=n
 # CONFIG_I440FX=n
 # CONFIG_Q35=n
 # CONFIG_Q35=n
 # CONFIG_MICROVM=n
 # CONFIG_MICROVM=n
+# CONFIG_NITRO_ENCLAVE=n

+ 7 - 3
configure

@@ -395,7 +395,11 @@ elif check_define _ARCH_PPC ; then
     cpu="ppc"
     cpu="ppc"
   fi
   fi
 elif check_define __mips__ ; then
 elif check_define __mips__ ; then
-  cpu="mips"
+  if check_define __mips64 ; then
+    cpu="mips64"
+  else
+    cpu="mips"
+  fi
 elif check_define __s390__ ; then
 elif check_define __s390__ ; then
   if check_define __s390x__ ; then
   if check_define __s390x__ ; then
     cpu="s390x"
     cpu="s390x"
@@ -1230,7 +1234,7 @@ EOF
       fi
       fi
     fi
     fi
 
 
-    case "$host_arch" in
+    case "$cpu" in
     arm)
     arm)
       # e.g. arm-unknown-linux-gnueabi, arm-unknown-linux-gnueabihf
       # e.g. arm-unknown-linux-gnueabi, arm-unknown-linux-gnueabihf
       write_c_skeleton
       write_c_skeleton
@@ -1278,7 +1282,7 @@ EOF
     test "$rust_arch" = arm && test "$rust_os" != linux && rust_arch=armv7
     test "$rust_arch" = arm && test "$rust_os" != linux && rust_arch=armv7
     ;;
     ;;
 
 
-  mips|mips64)
+  mips)
     # preserve ISA version (mipsisa64r6 etc.) and include endianness
     # preserve ISA version (mipsisa64r6 etc.) and include endianness
     rust_arch=${raw_cpu%el}
     rust_arch=${raw_cpu%el}
     test "$bigendian" = no && rust_arch=${rust_arch}el
     test "$bigendian" = no && rust_arch=${rust_arch}el

+ 1 - 1
docs/about/build-platforms.rst

@@ -41,7 +41,7 @@ Those hosts are officially supported, with various accelerators:
      - Accelerators
      - Accelerators
    * - Arm
    * - Arm
      - kvm (64 bit only), tcg, xen
      - kvm (64 bit only), tcg, xen
-   * - MIPS (little endian only)
+   * - MIPS (64 bit little endian only)
      - kvm, tcg
      - kvm, tcg
    * - PPC
    * - PPC
      - kvm, tcg
      - kvm, tcg

+ 8 - 4
docs/about/deprecated.rst

@@ -164,15 +164,19 @@ property types.
 Host Architectures
 Host Architectures
 ------------------
 ------------------
 
 
-BE MIPS (since 7.2)
-'''''''''''''''''''
+Big endian MIPS since 7.2; 32-bit little endian MIPS since 9.2
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 
 
 As Debian 10 ("Buster") moved into LTS the big endian 32 bit version of
 As Debian 10 ("Buster") moved into LTS the big endian 32 bit version of
 MIPS moved out of support making it hard to maintain our
 MIPS moved out of support making it hard to maintain our
 cross-compilation CI tests of the architecture. As we no longer have
 cross-compilation CI tests of the architecture. As we no longer have
 CI coverage support may bitrot away before the deprecation process
 CI coverage support may bitrot away before the deprecation process
-completes. The little endian variants of MIPS (both 32 and 64 bit) are
-still a supported host architecture.
+completes.
+
+Likewise, the little endian variant of 32 bit MIPS is not supported by
+Debian 13 ("Trixie") and newer.
+
+64 bit little endian MIPS is still a supported host architecture.
 
 
 System emulation on 32-bit x86 hosts (since 8.0)
 System emulation on 32-bit x86 hosts (since 8.0)
 ''''''''''''''''''''''''''''''''''''''''''''''''
 ''''''''''''''''''''''''''''''''''''''''''''''''

+ 78 - 0
docs/system/i386/nitro-enclave.rst

@@ -0,0 +1,78 @@
+'nitro-enclave' virtual machine (``nitro-enclave``)
+===================================================
+
+``nitro-enclave`` is a machine type which emulates an *AWS nitro enclave*
+virtual machine. `AWS nitro enclaves`_ is an Amazon EC2 feature that allows
+creating isolated execution environments, called enclaves, from Amazon EC2
+instances which are used for processing highly sensitive data. Enclaves have
+no persistent storage and no external networking. The enclave VMs are based
+on Firecracker microvm with a vhost-vsock device for communication with the
+parent EC2 instance that spawned it and a Nitro Secure Module (NSM) device
+for cryptographic attestation. The parent instance VM always has CID 3 while
+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
+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
+can do the userspace emulation, like `vhost-device-vsock`_ from rust-vmm crate,
+must be run alongside nitro-enclave for the vsock communication to work.
+
+``libcbor`` and ``gnutls`` are required dependencies for nitro-enclave machine
+support to be added when building QEMU from source.
+
+.. _AWS nitro enclaves: https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html
+.. _Enclave Image Format: https://github.com/aws/aws-nitro-enclaves-image-format
+.. _vhost-device-vsock: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock
+.. _Firecracker: https://firecracker-microvm.github.io
+
+Using the nitro-enclave machine type
+------------------------------------
+
+Machine-specific options
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+It supports the following machine-specific options:
+
+- nitro-enclave.vsock=string (required) (Id of the chardev from '-chardev' option that vhost-user-vsock device will use)
+- nitro-enclave.id=string (optional) (Set enclave identifier)
+- nitro-enclave.parent-role=string (optional) (Set parent instance IAM role ARN)
+- nitro-enclave.parent-id=string (optional) (Set parent instance identifier)
+
+
+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
+
+  $ vhost-device-vsock \
+     --vm guest-cid=4,forward-cid=1,forward-listen=9001+9002,socket=/tmp/vhost4.socket
+
+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
+parent VM know that it booted expecting a heartbeat (0xB7) response. So you must run
+a AF_VSOCK server on the host machine that listens on port 9000 and sends the heartbeat
+after it receives the heartbeat for enclave VM to boot successfully. You should run all
+the applications on the host machine that would typically be running in the parent EC2
+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:
+
+  $ qemu-system-x86_64 -M nitro-enclave,vsock=c,id=hello-world \
+     -kernel hello-world.eif -nographic -m 4G --enable-kvm -cpu host \
+     -chardev socket,id=c,path=/tmp/vhost4.socket
+
+In this example, the nitro-enclave VM has CID 4. If there are applications that
+connect to the enclave VM, run them on the host machine after enclave VM starts.
+You need to modify the applications to connect to CID 1 (instead of the enclave
+VM's CID) and use the forward-listen (e.g., 9001+9002) option of vhost-device-vsock
+to forward the ports they connect to.

+ 2 - 1
docs/system/target-i386.rst

@@ -14,8 +14,9 @@ Board-specific documentation
 .. toctree::
 .. toctree::
    :maxdepth: 1
    :maxdepth: 1
 
 
-   i386/microvm
    i386/pc
    i386/pc
+   i386/microvm
+   i386/nitro-enclave
 
 
 Architectural features
 Architectural features
 ~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~

+ 1 - 0
host/include/i386/host/cpuinfo.h

@@ -9,6 +9,7 @@
 /* Digested version of <cpuid.h> */
 /* Digested version of <cpuid.h> */
 
 
 #define CPUINFO_ALWAYS          (1u << 0)  /* so cpuinfo is nonzero */
 #define CPUINFO_ALWAYS          (1u << 0)  /* so cpuinfo is nonzero */
+#define CPUINFO_OSXSAVE         (1u << 1)
 #define CPUINFO_MOVBE           (1u << 2)
 #define CPUINFO_MOVBE           (1u << 2)
 #define CPUINFO_LZCNT           (1u << 3)
 #define CPUINFO_LZCNT           (1u << 3)
 #define CPUINFO_POPCNT          (1u << 4)
 #define CPUINFO_POPCNT          (1u << 4)

+ 4 - 0
hw/core/Kconfig

@@ -34,3 +34,7 @@ config REGISTER
 
 
 config SPLIT_IRQ
 config SPLIT_IRQ
     bool
     bool
+
+config EIF
+    bool
+    depends on LIBCBOR && GNUTLS

+ 719 - 0
hw/core/eif.c

@@ -0,0 +1,719 @@
+/*
+ * EIF (Enclave Image Format) related helpers
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "qapi/error.h"
+#include "crypto/hash.h"
+#include "crypto/x509-utils.h"
+#include <zlib.h> /* for crc32 */
+#include <cbor.h>
+
+#include "hw/core/eif.h"
+
+#define MAX_SECTIONS 32
+
+/* members are ordered according to field order in .eif file */
+typedef struct EifHeader {
+    uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */
+    uint16_t version;
+    uint16_t flags;
+    uint64_t default_memory;
+    uint64_t default_cpus;
+    uint16_t reserved;
+    uint16_t section_cnt;
+    uint64_t section_offsets[MAX_SECTIONS];
+    uint64_t section_sizes[MAX_SECTIONS];
+    uint32_t unused;
+    uint32_t eif_crc32;
+} QEMU_PACKED EifHeader;
+
+/* members are ordered according to field order in .eif file */
+typedef struct EifSectionHeader {
+    /*
+     * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature,
+     * 5 = metadata
+     */
+    uint16_t section_type;
+    uint16_t flags;
+    uint64_t section_size;
+} QEMU_PACKED EifSectionHeader;
+
+enum EifSectionTypes {
+    EIF_SECTION_INVALID = 0,
+    EIF_SECTION_KERNEL = 1,
+    EIF_SECTION_CMDLINE = 2,
+    EIF_SECTION_RAMDISK = 3,
+    EIF_SECTION_SIGNATURE = 4,
+    EIF_SECTION_METADATA = 5,
+    EIF_SECTION_MAX = 6,
+};
+
+static const char *section_type_to_string(uint16_t type)
+{
+    const char *str;
+    switch (type) {
+    case EIF_SECTION_INVALID:
+        str = "invalid";
+        break;
+    case EIF_SECTION_KERNEL:
+        str = "kernel";
+        break;
+    case EIF_SECTION_CMDLINE:
+        str = "cmdline";
+        break;
+    case EIF_SECTION_RAMDISK:
+        str = "ramdisk";
+        break;
+    case EIF_SECTION_SIGNATURE:
+        str = "signature";
+        break;
+    case EIF_SECTION_METADATA:
+        str = "metadata";
+        break;
+    default:
+        str = "unknown";
+        break;
+    }
+
+    return str;
+}
+
+static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc,
+                            Error **errp)
+{
+    size_t got;
+    size_t header_size = sizeof(*header);
+
+    got = fread(header, 1, header_size, f);
+    if (got != header_size) {
+        error_setg(errp, "Failed to read EIF header");
+        return false;
+    }
+
+    if (memcmp(header->magic, ".eif", 4) != 0) {
+        error_setg(errp, "Invalid EIF image. Magic mismatch.");
+        return false;
+    }
+
+    /* Exclude header->eif_crc32 field from CRC calculation */
+    *crc = crc32(*crc, (uint8_t *)header, header_size - 4);
+
+    header->version = be16_to_cpu(header->version);
+    header->flags = be16_to_cpu(header->flags);
+    header->default_memory = be64_to_cpu(header->default_memory);
+    header->default_cpus = be64_to_cpu(header->default_cpus);
+    header->reserved = be16_to_cpu(header->reserved);
+    header->section_cnt = be16_to_cpu(header->section_cnt);
+
+    for (int i = 0; i < MAX_SECTIONS; ++i) {
+        header->section_offsets[i] = be64_to_cpu(header->section_offsets[i]);
+    }
+
+    for (int i = 0; i < MAX_SECTIONS; ++i) {
+        header->section_sizes[i] = be64_to_cpu(header->section_sizes[i]);
+    }
+
+    header->unused = be32_to_cpu(header->unused);
+    header->eif_crc32 = be32_to_cpu(header->eif_crc32);
+    return true;
+}
+
+static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header,
+                                    uint32_t *crc, Error **errp)
+{
+    size_t got;
+    size_t section_header_size = sizeof(*section_header);
+
+    got = fread(section_header, 1, section_header_size, f);
+    if (got != section_header_size) {
+        error_setg(errp, "Failed to read EIF section header");
+        return false;
+    }
+
+    *crc = crc32(*crc, (uint8_t *)section_header, section_header_size);
+
+    section_header->section_type = be16_to_cpu(section_header->section_type);
+    section_header->flags = be16_to_cpu(section_header->flags);
+    section_header->section_size = be64_to_cpu(section_header->section_size);
+    return true;
+}
+
+/*
+ * Upon success, the caller is responsible for unlinking and freeing *tmp_path.
+ */
+static bool get_tmp_file(const char *template, char **tmp_path, Error **errp)
+{
+    int tmp_fd;
+
+    *tmp_path = NULL;
+    tmp_fd = g_file_open_tmp(template, tmp_path, NULL);
+    if (tmp_fd < 0 || *tmp_path == NULL) {
+        error_setg(errp, "Failed to create temporary file for template %s",
+                   template);
+        return false;
+    }
+
+    close(tmp_fd);
+    return true;
+}
+
+static void safe_fclose(FILE *f)
+{
+    if (f) {
+        fclose(f);
+    }
+}
+
+static void safe_unlink(char *f)
+{
+    if (f) {
+        unlink(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)
+{
+    size_t got;
+    FILE *tmp_file = NULL;
+
+    *kernel_path = NULL;
+    if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) {
+        goto cleanup;
+    }
+
+    tmp_file = fopen(*kernel_path, "wb");
+    if (tmp_file == NULL) {
+        error_setg_errno(errp, errno, "Failed to open temporary file %s",
+                         *kernel_path);
+        goto cleanup;
+    }
+
+    got = fread(kernel, 1, size, f);
+    if ((uint64_t) got != size) {
+        error_setg(errp, "Failed to read EIF kernel section data");
+        goto cleanup;
+    }
+
+    got = fwrite(kernel, 1, size, tmp_file);
+    if ((uint64_t) got != size) {
+        error_setg(errp, "Failed to write EIF kernel section data to temporary"
+                   " file");
+        goto cleanup;
+    }
+
+    *crc = crc32(*crc, kernel, size);
+    fclose(tmp_file);
+
+    return true;
+
+ cleanup:
+    safe_fclose(tmp_file);
+
+    safe_unlink(*kernel_path);
+    g_free(*kernel_path);
+    *kernel_path = NULL;
+
+    return false;
+}
+
+static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline,
+                             uint32_t *crc, Error **errp)
+{
+    size_t got = fread(cmdline, 1, size, f);
+    if ((uint64_t) got != size) {
+        error_setg(errp, "Failed to read EIF cmdline section data");
+        return false;
+    }
+
+    *crc = crc32(*crc, (uint8_t *)cmdline, size);
+    return true;
+}
+
+static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size,
+                             uint8_t *ramdisk, uint32_t *crc, Error **errp)
+{
+    size_t got;
+
+    got = fread(ramdisk, 1, size, eif);
+    if ((uint64_t) got != size) {
+        error_setg(errp, "Failed to read EIF ramdisk section data");
+        return false;
+    }
+
+    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;
+    }
+
+    *crc = crc32(*crc, ramdisk, size);
+    return true;
+}
+
+static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size,
+                                             uint8_t *sha384,
+                                             uint32_t *crc,
+                                             Error **errp)
+{
+    size_t got;
+    g_autofree uint8_t *sig = NULL;
+    g_autofree uint8_t *cert = NULL;
+    cbor_item_t *item = NULL;
+    cbor_item_t *pcr0 = NULL;
+    size_t len;
+    size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
+    struct cbor_pair *pair;
+    struct cbor_load_result result;
+    bool ret = false;
+
+    sig = g_malloc(size);
+    got = fread(sig, 1, size, eif);
+    if ((uint64_t) got != size) {
+        error_setg(errp, "Failed to read EIF signature section data");
+        goto cleanup;
+    }
+
+    *crc = crc32(*crc, sig, size);
+
+    item = cbor_load(sig, size, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        error_setg(errp, "Failed to load signature section data as CBOR");
+        goto cleanup;
+    }
+    if (!cbor_isa_array(item) || cbor_array_size(item) < 1) {
+        error_setg(errp, "Invalid signature CBOR");
+        goto cleanup;
+    }
+    pcr0 = cbor_array_get(item, 0);
+    if (!pcr0) {
+        error_setg(errp, "Failed to get PCR0 signature");
+        goto cleanup;
+    }
+    if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) {
+        error_setg(errp, "Invalid signature CBOR");
+        goto cleanup;
+    }
+    pair = cbor_map_handle(pcr0);
+    if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 ||
+        memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) {
+        error_setg(errp, "Invalid signautre CBOR");
+        goto cleanup;
+    }
+    if (!cbor_isa_array(pair->value)) {
+        error_setg(errp, "Invalid signature CBOR");
+        goto cleanup;
+    }
+    len = cbor_array_size(pair->value);
+    if (len == 0) {
+        error_setg(errp, "Invalid signature CBOR");
+        goto cleanup;
+    }
+    cert = g_malloc(len);
+    for (int i = 0; i < len; ++i) {
+        cbor_item_t *tmp = cbor_array_get(pair->value, i);
+        if (!tmp) {
+            error_setg(errp, "Invalid signature CBOR");
+            goto cleanup;
+        }
+        if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) {
+            cbor_decref(&tmp);
+            error_setg(errp, "Invalid signature CBOR");
+            goto cleanup;
+        }
+        cert[i] = cbor_get_uint8(tmp);
+        cbor_decref(&tmp);
+    }
+
+    if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384,
+                                          sha384, &hash_len, errp)) {
+        goto cleanup;
+    }
+
+    ret = true;
+
+ cleanup:
+    if (pcr0) {
+        cbor_decref(&pcr0);
+    }
+    if (item) {
+        cbor_decref(&item);
+    }
+    return ret;
+}
+
+/* Expects file to have offset 0 before this function is called */
+static long get_file_size(FILE *f, Error **errp)
+{
+    long size;
+
+    if (fseek(f, 0, SEEK_END) != 0) {
+        error_setg_errno(errp, errno, "Failed to seek to the end of file");
+        return -1;
+    }
+
+    size = ftell(f);
+    if (size == -1) {
+        error_setg_errno(errp, errno, "Failed to get offset");
+        return -1;
+    }
+
+    if (fseek(f, 0, SEEK_SET) != 0) {
+        error_setg_errno(errp, errno, "Failed to seek back to the start");
+        return -1;
+    }
+
+    return size;
+}
+
+static bool get_SHA384_digest(GList *list, uint8_t *digest, 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);
+    }
+}
+
+/*
+ * Upon success, the caller is reponsible for unlinking and freeing
+ * *kernel_path, *initrd_path and freeing *cmdline.
+ */
+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,
+                   bool *signature_found, Error **errp)
+{
+    FILE *f = NULL;
+    FILE *machine_initrd_f = NULL;
+    FILE *initrd_path_f = NULL;
+    long machine_initrd_size;
+    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;
+
+    *signature_found = false;
+    *kernel_path = *initrd_path = *cmdline = NULL;
+
+    f = fopen(eif_path, "rb");
+    if (f == NULL) {
+        error_setg_errno(errp, errno, "Failed to open %s", eif_path);
+        goto cleanup;
+    }
+
+    if (!read_eif_header(f, &eif_header, &crc, errp)) {
+        goto cleanup;
+    }
+
+    if (eif_header.version < 4) {
+        error_setg(errp, "Expected EIF version 4 or greater");
+        goto cleanup;
+    }
+
+    if (eif_header.flags != 0) {
+        error_setg(errp, "Expected EIF flags to be 0");
+        goto cleanup;
+    }
+
+    if (eif_header.section_cnt > MAX_SECTIONS) {
+        error_setg(errp, "EIF header section count must not be greater than "
+                   "%d but found %d", MAX_SECTIONS, eif_header.section_cnt);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < eif_header.section_cnt; ++i) {
+        EifSectionHeader hdr;
+        uint16_t section_type;
+
+        if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) {
+            error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file",
+                             eif_header.section_offsets[i]);
+            goto cleanup;
+        }
+
+        if (!read_eif_section_header(f, &hdr, &crc, errp)) {
+            goto cleanup;
+        }
+
+        if (hdr.flags != 0) {
+            error_setg(errp, "Expected EIF section header flags to be 0");
+            goto cleanup;
+        }
+
+        if (eif_header.section_sizes[i] != hdr.section_size) {
+            error_setg(errp, "EIF section size mismatch between header and "
+                       "section header: header %" PRIu64 ", section header %" PRIu64,
+                       eif_header.section_sizes[i],
+                       hdr.section_size);
+            goto cleanup;
+        }
+
+        section_type = hdr.section_type;
+
+        switch (section_type) {
+        case EIF_SECTION_KERNEL:
+            if (seen_sections[EIF_SECTION_KERNEL]) {
+                error_setg(errp, "Invalid EIF image. More than 1 kernel "
+                           "section");
+                goto cleanup;
+            }
+
+            ptr = g_malloc(hdr.section_size);
+
+            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)) {
+                goto cleanup;
+            }
+
+            break;
+        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");
+                goto cleanup;
+            }
+            size = hdr.section_size;
+            *cmdline = g_malloc(size + 1);
+            if (!read_eif_cmdline(f, size, *cmdline, &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:
+        {
+            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.
+                 */
+                if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) {
+                    goto cleanup;
+                }
+                initrd_path_f = fopen(*initrd_path, "wb");
+                if (initrd_path_f == NULL) {
+                    error_setg_errno(errp, errno, "Failed to open file %s",
+                                     *initrd_path);
+                    goto cleanup;
+                }
+            }
+
+            ptr = g_malloc(hdr.section_size);
+
+            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,
+                                  &crc, errp)) {
+                goto cleanup;
+            }
+
+            break;
+        }
+        case EIF_SECTION_SIGNATURE:
+            *signature_found = true;
+            if (!get_signature_fingerprint_sha384(f, hdr.section_size,
+                                                  fingerprint_sha384, &crc,
+                                                  errp)) {
+                goto cleanup;
+            }
+            break;
+        default:
+            /* other sections including invalid or unknown sections */
+        {
+            uint8_t *buf;
+            size_t got;
+            uint64_t size = hdr.section_size;
+            buf = g_malloc(size);
+            got = fread(buf, 1, size, f);
+            if ((uint64_t) got != size) {
+                g_free(buf);
+                error_setg(errp, "Failed to read EIF %s section data",
+                           section_type_to_string(section_type));
+                goto cleanup;
+            }
+            crc = crc32(crc, buf, size);
+            g_free(buf);
+            break;
+        }
+        }
+
+        if (section_type < EIF_SECTION_MAX) {
+            seen_sections[section_type] = true;
+        }
+    }
+
+    if (!seen_sections[EIF_SECTION_KERNEL]) {
+        error_setg(errp, "Invalid EIF image. No kernel section.");
+        goto cleanup;
+    }
+    if (!seen_sections[EIF_SECTION_CMDLINE]) {
+        error_setg(errp, "Invalid EIF image. No cmdline section.");
+        goto cleanup;
+    }
+    if (!seen_sections[EIF_SECTION_RAMDISK]) {
+        error_setg(errp, "Invalid EIF image. No ramdisk section.");
+        goto cleanup;
+    }
+
+    if (eif_header.eif_crc32 != crc) {
+        error_setg(errp, "CRC mismatch. Expected %u but header has %u.",
+                   crc, eif_header.eif_crc32);
+        goto cleanup;
+    }
+
+    /*
+     * Let's append the initrd file from "-initrd" option if any. Although
+     * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore.
+     * We have already done the crc mismatch check above this code.
+     */
+    if (machine_initrd) {
+        machine_initrd_f = fopen(machine_initrd, "rb");
+        if (machine_initrd_f == NULL) {
+            error_setg_errno(errp, errno, "Failed to open initrd file %s",
+                             machine_initrd);
+            goto cleanup;
+        }
+
+        machine_initrd_size = get_file_size(machine_initrd_f, errp);
+        if (machine_initrd_size == -1) {
+            goto cleanup;
+        }
+
+        ptr = g_malloc(machine_initrd_size);
+
+        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)) {
+            goto cleanup;
+        }
+    }
+
+    if (!get_SHA384_digest(iov_PCR0, image_sha384, errp)) {
+        goto cleanup;
+    }
+    if (!get_SHA384_digest(iov_PCR1, bootstrap_sha384, errp)) {
+        goto cleanup;
+    }
+    if (!get_SHA384_digest(iov_PCR2, app_sha384, 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);
+
+    safe_unlink(*kernel_path);
+    g_free(*kernel_path);
+    *kernel_path = NULL;
+
+    safe_unlink(*initrd_path);
+    g_free(*initrd_path);
+    *initrd_path = NULL;
+
+    g_free(*cmdline);
+    *cmdline = NULL;
+
+    return false;
+}

+ 22 - 0
hw/core/eif.h

@@ -0,0 +1,22 @@
+/*
+ * EIF (Enclave Image Format) related helpers
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef HW_CORE_EIF_H
+#define HW_CORE_EIF_H
+
+bool read_eif_file(const char *eif_path, const char *machine_initrd,
+                   char **kernel_path, char **initrd_path,
+                   char **kernel_cmdline, uint8_t *image_sha384,
+                   uint8_t *bootstrap_sha384, uint8_t *app_sha384,
+                   uint8_t *fingerprint_sha384, bool *signature_found,
+                   Error **errp);
+
+#endif
+

+ 38 - 33
hw/core/machine.c

@@ -1001,6 +1001,39 @@ void machine_add_audiodev_property(MachineClass *mc)
                                           "Audiodev to use for default machine devices");
                                           "Audiodev to use for default machine devices");
 }
 }
 
 
+static bool create_default_memdev(MachineState *ms, const char *path,
+                                  Error **errp)
+{
+    Object *obj;
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    bool r = false;
+
+    obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM);
+    if (path) {
+        if (!object_property_set_str(obj, "mem-path", path, errp)) {
+            goto out;
+        }
+    }
+    if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
+        goto out;
+    }
+    object_property_add_child(object_get_objects_root(), mc->default_ram_id,
+                              obj);
+    /* Ensure backend's memory region name is equal to mc->default_ram_id */
+    if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id",
+                             false, errp)) {
+        goto out;
+    }
+    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
+        goto out;
+    }
+    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
+
+out:
+    object_unref(obj);
+    return r;
+}
+
 static void machine_class_init(ObjectClass *oc, void *data)
 static void machine_class_init(ObjectClass *oc, void *data)
 {
 {
     MachineClass *mc = MACHINE_CLASS(oc);
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -1020,6 +1053,8 @@ static void machine_class_init(ObjectClass *oc, void *data)
      */
      */
     mc->numa_mem_align_shift = 23;
     mc->numa_mem_align_shift = 23;
 
 
+    mc->create_default_memdev = create_default_memdev;
+
     object_class_property_add_str(oc, "kernel",
     object_class_property_add_str(oc, "kernel",
         machine_get_kernel, machine_set_kernel);
         machine_get_kernel, machine_set_kernel);
     object_class_property_set_description(oc, "kernel",
     object_class_property_set_description(oc, "kernel",
@@ -1413,38 +1448,6 @@ MemoryRegion *machine_consume_memdev(MachineState *machine,
     return ret;
     return ret;
 }
 }
 
 
-static bool create_default_memdev(MachineState *ms, const char *path, Error **errp)
-{
-    Object *obj;
-    MachineClass *mc = MACHINE_GET_CLASS(ms);
-    bool r = false;
-
-    obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM);
-    if (path) {
-        if (!object_property_set_str(obj, "mem-path", path, errp)) {
-            goto out;
-        }
-    }
-    if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
-        goto out;
-    }
-    object_property_add_child(object_get_objects_root(), mc->default_ram_id,
-                              obj);
-    /* Ensure backend's memory region name is equal to mc->default_ram_id */
-    if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id",
-                             false, errp)) {
-        goto out;
-    }
-    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
-        goto out;
-    }
-    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
-
-out:
-    object_unref(obj);
-    return r;
-}
-
 const char *machine_class_default_cpu_type(MachineClass *mc)
 const char *machine_class_default_cpu_type(MachineClass *mc)
 {
 {
     if (mc->valid_cpu_types && !mc->valid_cpu_types[1]) {
     if (mc->valid_cpu_types && !mc->valid_cpu_types[1]) {
@@ -1548,7 +1551,9 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error *
                 machine_class->default_ram_id);
                 machine_class->default_ram_id);
             return;
             return;
         }
         }
-        if (!create_default_memdev(current_machine, mem_path, errp)) {
+
+        if (!machine_class->create_default_memdev(current_machine, mem_path,
+                                                  errp)) {
             return;
             return;
         }
         }
     }
     }

+ 1 - 0
hw/core/meson.build

@@ -24,6 +24,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c'))
 system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c'))
 system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c'))
 system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c'))
 system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c'))
 system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c'))
 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(
 system_ss.add(files(
   'cpu-sysemu.c',
   'cpu-sysemu.c',

+ 3 - 18
hw/core/qdev.c

@@ -146,31 +146,16 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
 
 
 DeviceState *qdev_new(const char *name)
 DeviceState *qdev_new(const char *name)
 {
 {
-    ObjectClass *oc = object_class_by_name(name);
-#ifdef CONFIG_MODULES
-    if (!oc) {
-        int rv = module_load_qom(name, &error_fatal);
-        if (rv > 0) {
-            oc = object_class_by_name(name);
-        } else {
-            error_report("could not find a module for type '%s'", name);
-            exit(1);
-        }
-    }
-#endif
-    if (!oc) {
-        error_report("unknown type '%s'", name);
-        abort();
-    }
     return DEVICE(object_new(name));
     return DEVICE(object_new(name));
 }
 }
 
 
 DeviceState *qdev_try_new(const char *name)
 DeviceState *qdev_try_new(const char *name)
 {
 {
-    if (!module_object_class_by_name(name)) {
+    ObjectClass *oc = module_object_class_by_name(name);
+    if (!oc) {
         return NULL;
         return NULL;
     }
     }
-    return DEVICE(object_new(name));
+    return DEVICE(object_new_with_class(oc));
 }
 }
 
 
 static QTAILQ_HEAD(, DeviceListener) device_listeners
 static QTAILQ_HEAD(, DeviceListener) device_listeners

+ 10 - 0
hw/i386/Kconfig

@@ -129,6 +129,16 @@ config MICROVM
     select USB_XHCI_SYSBUS
     select USB_XHCI_SYSBUS
     select I8254
     select I8254
 
 
+config NITRO_ENCLAVE
+    default y
+    depends on I386 && FDT # for MICROVM
+    depends on LIBCBOR && GNUTLS # for EIF and VIRTIO_NSM
+    depends on VHOST_USER # for VHOST_USER_VSOCK
+    select EIF
+    select MICROVM
+    select VHOST_USER_VSOCK
+    select VIRTIO_NSM
+
 config X86_IOMMU
 config X86_IOMMU
     bool
     bool
     depends on PC
     depends on PC

+ 1 - 0
hw/i386/meson.build

@@ -15,6 +15,7 @@ i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'),
                                       if_false: files('amd_iommu-stub.c'))
                                       if_false: files('amd_iommu-stub.c'))
 i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c'))
 i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c'))
 i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c'))
 i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c'))
+i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c'))
 i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c'))
 i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c'))
 i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c'))
 i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c'))
 i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c'))
 i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c'))

+ 5 - 1
hw/i386/microvm.c

@@ -283,6 +283,7 @@ static void microvm_devices_init(MicrovmMachineState *mms)
 
 
 static void microvm_memory_init(MicrovmMachineState *mms)
 static void microvm_memory_init(MicrovmMachineState *mms)
 {
 {
+    MicrovmMachineClass *mmc = MICROVM_MACHINE_GET_CLASS(mms);
     MachineState *machine = MACHINE(mms);
     MachineState *machine = MACHINE(mms);
     X86MachineState *x86ms = X86_MACHINE(mms);
     X86MachineState *x86ms = X86_MACHINE(mms);
     MemoryRegion *ram_below_4g, *ram_above_4g;
     MemoryRegion *ram_below_4g, *ram_above_4g;
@@ -328,7 +329,7 @@ static void microvm_memory_init(MicrovmMachineState *mms)
     rom_set_fw(fw_cfg);
     rom_set_fw(fw_cfg);
 
 
     if (machine->kernel_filename != NULL) {
     if (machine->kernel_filename != NULL) {
-        x86_load_linux(x86ms, fw_cfg, 0, true);
+        mmc->x86_load_linux(x86ms, fw_cfg, 0, true);
     }
     }
 
 
     if (mms->option_roms) {
     if (mms->option_roms) {
@@ -637,9 +638,12 @@ GlobalProperty microvm_properties[] = {
 static void microvm_class_init(ObjectClass *oc, void *data)
 static void microvm_class_init(ObjectClass *oc, void *data)
 {
 {
     X86MachineClass *x86mc = X86_MACHINE_CLASS(oc);
     X86MachineClass *x86mc = X86_MACHINE_CLASS(oc);
+    MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc);
     MachineClass *mc = MACHINE_CLASS(oc);
     MachineClass *mc = MACHINE_CLASS(oc);
     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
 
 
+    mmc->x86_load_linux = x86_load_linux;
+
     mc->init = microvm_machine_state_init;
     mc->init = microvm_machine_state_init;
 
 
     mc->family = "microvm_i386";
     mc->family = "microvm_i386";

+ 354 - 0
hw/i386/nitro_enclave.c

@@ -0,0 +1,354 @@
+/*
+ * AWS nitro-enclave machine
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+
+#include "chardev/char.h"
+#include "hw/sysbus.h"
+#include "hw/core/eif.h"
+#include "hw/i386/x86.h"
+#include "hw/i386/microvm.h"
+#include "hw/i386/nitro_enclave.h"
+#include "hw/virtio/virtio-mmio.h"
+#include "hw/virtio/virtio-nsm.h"
+#include "hw/virtio/vhost-user-vsock.h"
+#include "sysemu/hostmem.h"
+
+static BusState *find_free_virtio_mmio_bus(void)
+{
+    BusChild *kid;
+    BusState *bus = sysbus_get_default();
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        DeviceState *dev = kid->child;
+        if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) {
+            VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev));
+            VirtioBusState *mmio_virtio_bus = &mmio->bus;
+            BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
+            if (QTAILQ_EMPTY(&mmio_bus->children)) {
+                return mmio_bus;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static void vhost_user_vsock_init(NitroEnclaveMachineState *nems)
+{
+    DeviceState *dev = qdev_new(TYPE_VHOST_USER_VSOCK);
+    VHostUserVSock *vsock = VHOST_USER_VSOCK(dev);
+    BusState *bus;
+
+    if (!nems->vsock) {
+        error_report("A valid chardev id for vhost-user-vsock device must be "
+                     "provided using the 'vsock' machine option");
+        exit(1);
+    }
+
+    bus = find_free_virtio_mmio_bus();
+    if (!bus) {
+        error_report("Failed to find bus for vhost-user-vsock device");
+        exit(1);
+    }
+
+    Chardev *chardev = qemu_chr_find(nems->vsock);
+    if (!chardev) {
+        error_report("Failed to find chardev with id %s", nems->vsock);
+        exit(1);
+    }
+
+    vsock->conf.chardev.chr = chardev;
+
+    qdev_realize_and_unref(dev, bus, &error_fatal);
+}
+
+static void virtio_nsm_init(NitroEnclaveMachineState *nems)
+{
+    DeviceState *dev = qdev_new(TYPE_VIRTIO_NSM);
+    VirtIONSM *vnsm = VIRTIO_NSM(dev);
+    BusState *bus = find_free_virtio_mmio_bus();
+
+    if (!bus) {
+        error_report("Failed to find bus for virtio-nsm device.");
+        exit(1);
+    }
+
+    qdev_prop_set_string(dev, "module-id", nems->id);
+
+    qdev_realize_and_unref(dev, bus, &error_fatal);
+    nems->vnsm = vnsm;
+}
+
+static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems)
+{
+    vhost_user_vsock_init(nems);
+    virtio_nsm_init(nems);
+}
+
+static void nitro_enclave_machine_state_init(MachineState *machine)
+{
+    NitroEnclaveMachineClass *ne_class =
+        NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
+    NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
+
+    ne_class->parent_init(machine);
+    nitro_enclave_devices_init(ne_state);
+}
+
+static void nitro_enclave_machine_reset(MachineState *machine, ResetType type)
+{
+    NitroEnclaveMachineClass *ne_class =
+        NITRO_ENCLAVE_MACHINE_GET_CLASS(machine);
+    NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine);
+
+    ne_class->parent_reset(machine, 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,
+                               QCRYPTO_HASH_DIGEST_LEN_SHA384);
+    /* PCR1 */
+    ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_sha384,
+                               QCRYPTO_HASH_DIGEST_LEN_SHA384);
+    /* PCR2 */
+    ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_sha384,
+                               QCRYPTO_HASH_DIGEST_LEN_SHA384);
+    /* PCR3 */
+    if (ne_state->parent_role) {
+        ne_state->vnsm->extend_pcr(ne_state->vnsm, 3,
+                                   (uint8_t *) ne_state->parent_role,
+                                   strlen(ne_state->parent_role));
+    }
+    /* PCR4 */
+    if (ne_state->parent_id) {
+        ne_state->vnsm->extend_pcr(ne_state->vnsm, 4,
+                                   (uint8_t *) ne_state->parent_id,
+                                   strlen(ne_state->parent_id));
+    }
+    /* PCR8 */
+    if (ne_state->signature_found) {
+        ne_state->vnsm->extend_pcr(ne_state->vnsm, 8,
+                                   ne_state->fingerprint_sha384,
+                                   QCRYPTO_HASH_DIGEST_LEN_SHA384);
+    }
+
+    /* First 16 PCRs are locked from boot and reserved for nitro enclave */
+    for (int i = 0; i < 16; ++i) {
+        ne_state->vnsm->lock_pcr(ne_state->vnsm, i);
+    }
+}
+
+static void nitro_enclave_machine_initfn(Object *obj)
+{
+    MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+    X86MachineState *x86ms = X86_MACHINE(obj);
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    nems->id = g_strdup("i-234-enc5678");
+
+    /* AWS nitro enclaves have PCIE and ACPI disabled */
+    mms->pcie = ON_OFF_AUTO_OFF;
+    x86ms->acpi = ON_OFF_AUTO_OFF;
+}
+
+static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg,
+                         int acpi_data_size, bool pvh_enabled)
+{
+    Error *err = NULL;
+    char *eif_kernel, *eif_initrd, *eif_cmdline;
+    MachineState *machine = MACHINE(x86ms);
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(x86ms);
+
+    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->signature_found), &err)) {
+        error_report_err(err);
+        exit(1);
+    }
+
+    g_free(machine->kernel_filename);
+    machine->kernel_filename = eif_kernel;
+    g_free(machine->initrd_filename);
+    machine->initrd_filename = eif_initrd;
+
+    /*
+     * If kernel cmdline argument was provided, let's concatenate it to the
+     * extracted EIF kernel cmdline.
+     */
+    if (machine->kernel_cmdline != NULL) {
+        char *cmd = g_strdup_printf("%s %s", eif_cmdline,
+                                    machine->kernel_cmdline);
+        g_free(eif_cmdline);
+        g_free(machine->kernel_cmdline);
+        machine->kernel_cmdline = cmd;
+    } else {
+        machine->kernel_cmdline = eif_cmdline;
+    }
+
+    x86_load_linux(x86ms, fw_cfg, 0, true);
+
+    unlink(machine->kernel_filename);
+    unlink(machine->initrd_filename);
+    return;
+}
+
+static bool create_memfd_backend(MachineState *ms, const char *path,
+                                 Error **errp)
+{
+    Object *obj;
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    bool r = false;
+
+    obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
+    if (!object_property_set_int(obj, "size", ms->ram_size, errp)) {
+        goto out;
+    }
+    object_property_add_child(object_get_objects_root(), mc->default_ram_id,
+                              obj);
+
+    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
+        goto out;
+    }
+    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
+
+out:
+    object_unref(obj);
+    return r;
+}
+
+static char *nitro_enclave_get_vsock_chardev_id(Object *obj, Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    return g_strdup(nems->vsock);
+}
+
+static void nitro_enclave_set_vsock_chardev_id(Object *obj, const char *value,
+                                               Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    g_free(nems->vsock);
+    nems->vsock = g_strdup(value);
+}
+
+static char *nitro_enclave_get_id(Object *obj, Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    return g_strdup(nems->id);
+}
+
+static void nitro_enclave_set_id(Object *obj, const char *value,
+                                            Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    g_free(nems->id);
+    nems->id = g_strdup(value);
+}
+
+static char *nitro_enclave_get_parent_role(Object *obj, Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    return g_strdup(nems->parent_role);
+}
+
+static void nitro_enclave_set_parent_role(Object *obj, const char *value,
+                                          Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    g_free(nems->parent_role);
+    nems->parent_role = g_strdup(value);
+}
+
+static char *nitro_enclave_get_parent_id(Object *obj, Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    return g_strdup(nems->parent_id);
+}
+
+static void nitro_enclave_set_parent_id(Object *obj, const char *value,
+                                        Error **errp)
+{
+    NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj);
+
+    g_free(nems->parent_id);
+    nems->parent_id = g_strdup(value);
+}
+
+static void nitro_enclave_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+    MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc);
+    NitroEnclaveMachineClass *nemc = NITRO_ENCLAVE_MACHINE_CLASS(oc);
+
+    mmc->x86_load_linux = x86_load_eif;
+
+    mc->family = "nitro_enclave_i386";
+    mc->desc = "AWS Nitro Enclave";
+
+    nemc->parent_init = mc->init;
+    mc->init = nitro_enclave_machine_state_init;
+
+    nemc->parent_reset = mc->reset;
+    mc->reset = nitro_enclave_machine_reset;
+
+    mc->create_default_memdev = create_memfd_backend;
+
+    object_class_property_add_str(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
+                                  nitro_enclave_get_vsock_chardev_id,
+                                  nitro_enclave_set_vsock_chardev_id);
+    object_class_property_set_description(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID,
+                                          "Set chardev id for vhost-user-vsock "
+                                          "device");
+
+    object_class_property_add_str(oc, NITRO_ENCLAVE_ID, nitro_enclave_get_id,
+                                  nitro_enclave_set_id);
+    object_class_property_set_description(oc, NITRO_ENCLAVE_ID,
+                                          "Set enclave identifier");
+
+    object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ROLE,
+                                  nitro_enclave_get_parent_role,
+                                  nitro_enclave_set_parent_role);
+    object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ROLE,
+                                          "Set parent instance IAM role ARN");
+
+    object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ID,
+                                  nitro_enclave_get_parent_id,
+                                  nitro_enclave_set_parent_id);
+    object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ID,
+                                          "Set parent instance identifier");
+}
+
+static const TypeInfo nitro_enclave_machine_info = {
+    .name          = TYPE_NITRO_ENCLAVE_MACHINE,
+    .parent        = TYPE_MICROVM_MACHINE,
+    .instance_size = sizeof(NitroEnclaveMachineState),
+    .instance_init = nitro_enclave_machine_initfn,
+    .class_size    = sizeof(NitroEnclaveMachineClass),
+    .class_init    = nitro_enclave_class_init,
+};
+
+static void nitro_enclave_machine_init(void)
+{
+    type_register_static(&nitro_enclave_machine_info);
+}
+type_init(nitro_enclave_machine_init);

+ 4 - 0
hw/virtio/Kconfig

@@ -6,6 +6,10 @@ config VIRTIO_RNG
     default y
     default y
     depends on VIRTIO
     depends on VIRTIO
 
 
+config VIRTIO_NSM
+   bool
+   depends on LIBCBOR && VIRTIO
+
 config VIRTIO_IOMMU
 config VIRTIO_IOMMU
     bool
     bool
     default y
     default y

+ 321 - 0
hw/virtio/cbor-helpers.c

@@ -0,0 +1,321 @@
+/*
+ * QEMU CBOR helpers
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#include "hw/virtio/cbor-helpers.h"
+
+bool qemu_cbor_map_add(cbor_item_t *map, cbor_item_t *key, cbor_item_t *value)
+{
+    bool success = false;
+    struct cbor_pair pair = (struct cbor_pair) {
+        .key = cbor_move(key),
+        .value = cbor_move(value)
+    };
+
+    success = cbor_map_add(map, pair);
+    if (!success) {
+        cbor_incref(pair.key);
+        cbor_incref(pair.value);
+    }
+
+    return success;
+}
+
+bool qemu_cbor_array_push(cbor_item_t *array, cbor_item_t *value)
+{
+    bool success = false;
+
+    success = cbor_array_push(array, cbor_move(value));
+    if (!success) {
+        cbor_incref(value);
+    }
+
+    return success;
+}
+
+bool qemu_cbor_add_bool_to_map(cbor_item_t *map, const char *key, bool value)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_bool(value);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_uint8_to_map(cbor_item_t *map, const char *key,
+                                uint8_t value)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_uint8(value);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_map_to_map(cbor_item_t *map, const char *key,
+                              size_t nested_map_size,
+                              cbor_item_t **nested_map)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_new_definite_map(nested_map_size);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+    *nested_map = value_cbor;
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_bytestring_to_map(cbor_item_t *map, const char *key,
+                                     uint8_t *arr, size_t len)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_bytestring(arr, len);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_null_to_map(cbor_item_t *map, const char *key)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_new_null();
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_string_to_map(cbor_item_t *map, const char *key,
+                                 const char *value)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_string(value);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_uint8_array_to_map(cbor_item_t *map, const char *key,
+                                      uint8_t *arr, size_t len)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_new_definite_array(len);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+
+    for (int i = 0; i < len; ++i) {
+        cbor_item_t *tmp = cbor_build_uint8(arr[i]);
+        if (!tmp) {
+            goto cleanup;
+        }
+        if (!qemu_cbor_array_push(value_cbor, tmp)) {
+            cbor_decref(&tmp);
+            goto cleanup;
+        }
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_uint8_key_bytestring_to_map(cbor_item_t *map, uint8_t key,
+                                               uint8_t *buf, size_t len)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_uint8(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_bytestring(buf, len);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+bool qemu_cbor_add_uint64_to_map(cbor_item_t *map, const char *key,
+                                 uint64_t value)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+
+    key_cbor = cbor_build_string(key);
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_build_uint64(value);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}

+ 2 - 0
hw/virtio/meson.build

@@ -54,6 +54,7 @@ specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c
 specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c'))
 specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c'))
 specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
 specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c'))
 specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
 specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c'))
+specific_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm.c', 'cbor-helpers.c'), libcbor])
 specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
 specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
 specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c'))
 specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c'))
 specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c'))
 specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c'))
@@ -70,6 +71,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto-pc
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng-pci.c'))
+virtio_pci_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm-pci.c', 'cbor-helpers.c'), libcbor])
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio-scsi-pci.c'))
 virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio-scsi-pci.c'))

+ 73 - 0
hw/virtio/virtio-nsm-pci.c

@@ -0,0 +1,73 @@
+/*
+ * AWS Nitro Secure Module (NSM) device
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-nsm.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qom/object.h"
+
+typedef struct VirtIONsmPCI VirtIONsmPCI;
+
+#define TYPE_VIRTIO_NSM_PCI "virtio-nsm-pci-base"
+DECLARE_INSTANCE_CHECKER(VirtIONsmPCI, VIRTIO_NSM_PCI,
+                         TYPE_VIRTIO_NSM_PCI)
+
+struct VirtIONsmPCI {
+    VirtIOPCIProxy parent_obj;
+    VirtIONSM vdev;
+};
+
+static void virtio_nsm_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+    VirtIONsmPCI *vnsm = VIRTIO_NSM_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&vnsm->vdev);
+
+    virtio_pci_force_virtio_1(vpci_dev);
+
+    if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) {
+        return;
+    }
+}
+
+static void virtio_nsm_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+
+    k->realize = virtio_nsm_pci_realize;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static void virtio_nsm_initfn(Object *obj)
+{
+    VirtIONsmPCI *dev = VIRTIO_NSM_PCI(obj);
+
+    virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+                                TYPE_VIRTIO_NSM);
+}
+
+static const VirtioPCIDeviceTypeInfo virtio_nsm_pci_info = {
+    .base_name             = TYPE_VIRTIO_NSM_PCI,
+    .generic_name          = "virtio-nsm-pci",
+    .instance_size = sizeof(VirtIONsmPCI),
+    .instance_init = virtio_nsm_initfn,
+    .class_init    = virtio_nsm_pci_class_init,
+};
+
+static void virtio_nsm_pci_register(void)
+{
+    virtio_pci_types_register(&virtio_nsm_pci_info);
+}
+
+type_init(virtio_nsm_pci_register)

+ 1732 - 0
hw/virtio/virtio-nsm.c

@@ -0,0 +1,1732 @@
+/*
+ * AWS Nitro Secure Module (NSM) device
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/iov.h"
+#include "qemu/guest-random.h"
+#include "qapi/error.h"
+
+#include "crypto/hash.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-nsm.h"
+#include "hw/virtio/cbor-helpers.h"
+#include "standard-headers/linux/virtio_ids.h"
+
+#define NSM_REQUEST_MAX_SIZE      0x1000
+#define NSM_RESPONSE_BUF_SIZE     0x3000
+#define NSM_RND_BUF_SIZE          256
+
+enum NSMResponseTypes {
+    NSM_SUCCESS = 0,
+    NSM_INVALID_ARGUMENT = 1,
+    NSM_INVALID_INDEX = 2,
+    NSM_READONLY_INDEX = 3,
+    NSM_INVALID_OPERATION = 4,
+    NSM_BUFFER_TOO_SMALL = 5,
+    NSM_INPUT_TOO_LARGE = 6,
+    NSM_INTERNAL_ERROR = 7,
+};
+
+static const char *error_string(enum NSMResponseTypes type)
+{
+    const char *str;
+    switch (type) {
+    case NSM_INVALID_ARGUMENT:
+        str = "InvalidArgument";
+        break;
+    case NSM_INVALID_INDEX:
+        str = "InvalidIndex";
+        break;
+    case NSM_READONLY_INDEX:
+        str = "ReadOnlyIndex";
+        break;
+    case NSM_INVALID_OPERATION:
+        str = "InvalidOperation";
+        break;
+    case NSM_BUFFER_TOO_SMALL:
+        str = "BufferTooSmall";
+        break;
+    case NSM_INPUT_TOO_LARGE:
+        str = "InputTooLarge";
+        break;
+    default:
+        str = "InternalError";
+        break;
+    }
+
+    return str;
+}
+
+/*
+ * Error response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("Error"),
+ *     value = String(error_name)
+ *   }
+ * }
+ *
+ * where error_name can be one of the following:
+ *   InvalidArgument
+ *   InvalidIndex
+ *   InvalidResponse
+ *   ReadOnlyIndex
+ *   InvalidOperation
+ *   BufferTooSmall
+ *   InputTooLarge
+ *   InternalError
+ */
+
+static bool error_response(struct iovec *response, enum NSMResponseTypes error,
+                           Error **errp)
+{
+    cbor_item_t *root;
+    size_t len;
+    bool r = false;
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_string_to_map(root, "Error", error_string(error))) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        error_setg(errp, "Response buffer is small for %s response",
+                   error_string(error));
+        goto out;
+    }
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize %s response", error_string(error));
+    goto out;
+}
+
+/*
+ * GetRandom response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("GetRandom"),
+ *     value = Map(1) {
+ *       key = String("random"),
+ *       value = Byte_String()
+ *     }
+ *   }
+ * }
+ */
+static bool handle_get_random(VirtIONSM *vnsm, struct iovec *request,
+                              struct iovec *response, Error **errp)
+{
+    cbor_item_t *root, *nested_map;
+    size_t len;
+    uint8_t rnd[NSM_RND_BUF_SIZE];
+    bool r = false;
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_map_to_map(root, "GetRandom", 1, &nested_map)) {
+        goto err;
+    }
+
+    qemu_guest_getrandom_nofail(rnd, NSM_RND_BUF_SIZE);
+
+    if (!qemu_cbor_add_bytestring_to_map(nested_map, "random", rnd,
+                                         NSM_RND_BUF_SIZE)) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize GetRandom response");
+    goto out;
+}
+
+/*
+ * DescribeNSM response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("DescribeNSM"),
+ *     value = Map(7) {
+ *       key = String("digest"),
+ *       value = String("SHA384"),
+ *       key = String("max_pcrs"),
+ *       value = Uint8(32),
+ *       key = String("module_id"),
+ *       value = String("i-1234-enc5678"),
+ *       key = String("locked_pcrs"),
+ *       value = Array<Uint8>(),
+ *       key = String("version_major"),
+ *       value = Uint8(1),
+ *       key = String("version_minor"),
+ *       value = Uint8(0),
+ *       key = String("version_patch"),
+ *       value = Uint8(0)
+ *     }
+ *   }
+ * }
+ */
+static bool handle_describe_nsm(VirtIONSM *vnsm, struct iovec *request,
+                                struct iovec *response, Error **errp)
+{
+    cbor_item_t *root, *nested_map;
+    uint16_t locked_pcrs_cnt = 0;
+    uint8_t locked_pcrs_ind[NSM_MAX_PCRS];
+    size_t len;
+    bool r = false;
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_map_to_map(root, "DescribeNSM", 7, &nested_map)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_string_to_map(nested_map, "digest", vnsm->digest)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_uint8_to_map(nested_map, "max_pcrs", vnsm->max_pcrs)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_string_to_map(nested_map, "module_id",
+                                     vnsm->module_id)) {
+        goto err;
+    }
+
+    for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
+        if (vnsm->pcrs[i].locked) {
+            locked_pcrs_ind[locked_pcrs_cnt++] = i;
+        }
+    }
+    if (!qemu_cbor_add_uint8_array_to_map(nested_map, "locked_pcrs",
+                                          locked_pcrs_ind, locked_pcrs_cnt)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_major",
+                                    vnsm->version_major)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_minor",
+                                    vnsm->version_minor)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_uint8_to_map(nested_map, "version_patch",
+                                    vnsm->version_patch)) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize DescribeNSM response");
+    goto out;
+}
+
+/*
+ * DescribePCR request structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("DescribePCR"),
+ *     value = Map(1) {
+ *       key = String("index"),
+ *       value = Uint8(pcr)
+ *     }
+ *   }
+ * }
+ */
+typedef struct NSMDescribePCRReq {
+    uint8_t index;
+} NSMDescribePCRReq;
+
+static enum NSMResponseTypes get_nsm_describe_pcr_req(
+    uint8_t *req, size_t len,
+    NSMDescribePCRReq *nsm_req)
+{
+    size_t size;
+    uint8_t *str;
+    struct cbor_pair *pair;
+    cbor_item_t *item = NULL;
+    struct cbor_load_result result;
+    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
+
+    item = cbor_load(req, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(item);
+    if (!cbor_isa_map(pair->value)) {
+        goto cleanup;
+    }
+    size = cbor_map_size(pair->value);
+    if (size < 1) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(pair->value);
+    for (int i = 0; i < size; ++i) {
+        if (!cbor_isa_string(pair[i].key)) {
+            continue;
+        }
+
+        str = cbor_string_handle(pair[i].key);
+        if (str && cbor_string_length(pair[i].key) == 5 &&
+            memcmp(str, "index", 5) == 0) {
+            if (!cbor_isa_uint(pair[i].value) ||
+                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
+                break;
+            }
+
+            nsm_req->index = cbor_get_uint8(pair[i].value);
+            r = NSM_SUCCESS;
+            break;
+        }
+    }
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return r;
+}
+
+/*
+ * DescribePCR response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("DescribePCR"),
+ *     value = Map(2) {
+ *       key = String("data"),
+ *       value = Byte_String(),
+ *       key = String("lock"),
+ *       value = Bool()
+ *     }
+ *   }
+ * }
+ */
+static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request,
+                                struct iovec *response, Error **errp)
+{
+    cbor_item_t *root = NULL;
+    cbor_item_t *nested_map;
+    size_t len;
+    NSMDescribePCRReq nsm_req;
+    enum NSMResponseTypes type;
+    struct PCRInfo *pcr;
+    bool r = false;
+
+    type = get_nsm_describe_pcr_req(request->iov_base, request->iov_len,
+                                    &nsm_req);
+    if (type != NSM_SUCCESS) {
+        if (error_response(response, type, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+    if (nsm_req.index >= vnsm->max_pcrs) {
+        if (error_response(response, NSM_INVALID_INDEX, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+    pcr = &(vnsm->pcrs[nsm_req.index]);
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_map_to_map(root, "DescribePCR", 2, &nested_map)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
+                                         QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_bool_to_map(nested_map, "lock", pcr->locked)) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize DescribePCR response");
+    goto out;
+}
+
+/*
+ * ExtendPCR request structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("ExtendPCR"),
+ *     value = Map(2) {
+ *       key = String("index"),
+ *       value = Uint8(pcr),
+ *       key = String("data"),
+ *       value = Byte_String(data),
+ *     }
+ *   }
+ * }
+ */
+typedef struct NSMExtendPCRReq {
+    uint8_t index;
+    uint16_t data_len;
+    uint8_t data[NSM_REQUEST_MAX_SIZE];
+} NSMExtendPCRReq;
+
+static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len,
+                                                    NSMExtendPCRReq *nsm_req)
+{
+    cbor_item_t *item = NULL;
+    size_t size ;
+    uint8_t *str;
+    bool index_found = false;
+    bool data_found = false;
+    struct cbor_pair *pair;
+    struct cbor_load_result result;
+    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
+
+    item = cbor_load(req, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(item);
+    if (!cbor_isa_map(pair->value)) {
+        goto cleanup;
+    }
+    size = cbor_map_size(pair->value);
+    if (size < 2) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(pair->value);
+    for (int i = 0; i < size; ++i) {
+        if (!cbor_isa_string(pair[i].key)) {
+            continue;
+        }
+        str = cbor_string_handle(pair[i].key);
+        if (!str) {
+            continue;
+        }
+
+        if (cbor_string_length(pair[i].key) == 5 &&
+            memcmp(str, "index", 5) == 0) {
+            if (!cbor_isa_uint(pair[i].value) ||
+                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
+                goto cleanup;
+            }
+            nsm_req->index = cbor_get_uint8(pair[i].value);
+            index_found = true;
+            continue;
+        }
+
+        if (cbor_string_length(pair[i].key) == 4 &&
+            memcmp(str, "data", 4) == 0) {
+            if (!cbor_isa_bytestring(pair[i].value)) {
+                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
+             * any request. So it's safe to copy.
+             */
+            memcpy(nsm_req->data, str, nsm_req->data_len);
+            data_found = true;
+            continue;
+        }
+    }
+
+    if (index_found && data_found) {
+        r = NSM_SUCCESS;
+    }
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return r;
+}
+
+/*
+ * ExtendPCR response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("ExtendPCR"),
+ *     value = Map(1) {
+ *       key = String("data"),
+ *       value = Byte_String()
+ *     }
+ *   }
+ * }
+ */
+static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
+                              struct iovec *response, Error **errp)
+{
+    cbor_item_t *root = NULL;
+    cbor_item_t *nested_map;
+    size_t len;
+    struct PCRInfo *pcr;
+    enum NSMResponseTypes type;
+    bool r = false;
+    g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
+
+    type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
+                                  nsm_req);
+    if (type != NSM_SUCCESS) {
+        if (error_response(response, type, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+    if (nsm_req->index >= vnsm->max_pcrs) {
+        if (error_response(response, NSM_INVALID_INDEX, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    pcr = &(vnsm->pcrs[nsm_req->index]);
+
+    if (pcr->locked) {
+        if (error_response(response, NSM_READONLY_INDEX, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
+                          nsm_req->data_len)) {
+        if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
+        goto err;
+    }
+
+    if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
+                                         QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize DescribePCR response");
+    goto out;
+}
+
+/*
+ * LockPCR request structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("LockPCR"),
+ *     value = Map(1) {
+ *       key = String("index"),
+ *       value = Uint8(pcr)
+ *     }
+ *   }
+ * }
+ */
+typedef struct NSMLockPCRReq {
+    uint8_t index;
+} NSMLockPCRReq;
+
+static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
+                                                  NSMLockPCRReq *nsm_req)
+{
+    cbor_item_t *item = NULL;
+    size_t size;
+    uint8_t *str;
+    struct cbor_pair *pair;
+    struct cbor_load_result result;
+    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
+
+    item = cbor_load(req, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(item);
+    if (!cbor_isa_map(pair->value)) {
+        goto cleanup;
+    }
+    size = cbor_map_size(pair->value);
+    if (size < 1) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(pair->value);
+    for (int i = 0; i < size; ++i) {
+        if (!cbor_isa_string(pair[i].key)) {
+            continue;
+        }
+        str = cbor_string_handle(pair[i].key);
+        if (str && cbor_string_length(pair[i].key) == 5 &&
+            memcmp(str, "index", 5) == 0) {
+            if (!cbor_isa_uint(pair[i].value) ||
+                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
+                break;
+            }
+
+            nsm_req->index = cbor_get_uint8(pair[i].value);
+            r = NSM_SUCCESS;
+            break;
+        }
+    }
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return r;
+}
+
+/*
+ * LockPCR success response structure:
+ * {
+ *   String("LockPCR")
+ * }
+ */
+static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
+                            struct iovec *response, Error **errp)
+{
+    cbor_item_t *root = NULL;
+    size_t len;
+    NSMLockPCRReq nsm_req;
+    enum NSMResponseTypes type;
+    struct PCRInfo *pcr;
+    bool r = false;
+
+    type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
+    if (type != NSM_SUCCESS) {
+        if (error_response(response, type, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+    if (nsm_req.index >= vnsm->max_pcrs) {
+        if (error_response(response, NSM_INVALID_INDEX, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+
+    pcr = &(vnsm->pcrs[nsm_req.index]);
+
+    if (pcr->locked) {
+        if (error_response(response, NSM_READONLY_INDEX, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+
+    pcr->locked = true;
+
+    root = cbor_build_string("LockPCR");
+    if (!root) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+
+    response->iov_len = len;
+    r = true;
+    goto cleanup;
+
+ err:
+    error_setg(errp, "Failed to initialize LockPCR response");
+
+ cleanup:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+}
+
+/*
+ * LockPCRs request structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("LockPCRs"),
+ *     value = Map(1) {
+ *       key = String("range"),
+ *       value = Uint8(pcr)
+ *     }
+ *   }
+ * }
+ */
+typedef struct NSMLockPCRsReq {
+    uint16_t range;
+} NSMLockPCRsReq;
+
+static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
+                                                   NSMLockPCRsReq *nsm_req)
+{
+    cbor_item_t *item = NULL;
+    size_t size;
+    uint8_t *str;
+    struct cbor_pair *pair;
+    struct cbor_load_result result;
+    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
+
+    item = cbor_load(req, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(item);
+    if (!cbor_isa_map(pair->value)) {
+        goto cleanup;
+    }
+    size = cbor_map_size(pair->value);
+    if (size < 1) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(pair->value);
+    for (int i = 0; i < size; ++i) {
+        if (!cbor_isa_string(pair[i].key)) {
+            continue;
+        }
+        str = cbor_string_handle(pair[i].key);
+        if (str && cbor_string_length(pair[i].key) == 5 &&
+            memcmp(str, "range", 5) == 0) {
+            if (!cbor_isa_uint(pair[i].value) ||
+                cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
+                break;
+            }
+
+            nsm_req->range = cbor_get_uint8(pair[i].value);
+            r = NSM_SUCCESS;
+            break;
+        }
+    }
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return r;
+}
+
+/*
+ * LockPCRs success response structure:
+ * {
+ *   String("LockPCRs")
+ * }
+ */
+static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
+                             struct iovec *response, Error **errp)
+{
+    cbor_item_t *root = NULL;
+    size_t len;
+    NSMLockPCRsReq nsm_req;
+    enum NSMResponseTypes type;
+    bool r = false;
+
+    type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
+    if (type != NSM_SUCCESS) {
+        if (error_response(response, type, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+    if (nsm_req.range > vnsm->max_pcrs) {
+        if (error_response(response, NSM_INVALID_INDEX, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+
+    for (int i = 0; i < nsm_req.range; ++i) {
+        vnsm->pcrs[i].locked = true;
+    }
+
+    root = cbor_build_string("LockPCRs");
+    if (!root) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
+            r = true;
+        }
+        goto cleanup;
+    }
+
+    response->iov_len = len;
+    r = true;
+    goto cleanup;
+
+ err:
+    error_setg(errp, "Failed to initialize response");
+
+ cleanup:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+}
+
+/*
+ * Attestation request structure:
+ *
+ *   Map(1) {
+ *     key = String("Attestation"),
+ *     value = Map(3) {
+ *       key = String("user_data"),
+ *       value = Byte_String() || null, // Optional
+ *       key = String("nonce"),
+ *       value = Byte_String() || null, // Optional
+ *       key = String("public_key"),
+ *       value = Byte_String() || null, // Optional
+ *     }
+ *   }
+ * }
+ */
+
+struct AttestationProperty {
+    bool is_null; /* True if property is not present in map or is null */
+    uint16_t len;
+    uint8_t buf[NSM_REQUEST_MAX_SIZE];
+};
+
+typedef struct NSMAttestationReq {
+    struct AttestationProperty public_key;
+    struct AttestationProperty user_data;
+    struct AttestationProperty nonce;
+} NSMAttestationReq;
+
+static bool fill_attestation_property(struct AttestationProperty *prop,
+                                      cbor_item_t *value)
+{
+    uint8_t *str;
+    bool ret = false;
+
+    if (cbor_is_null(value)) {
+        prop->is_null = true;
+        ret = true;
+        goto out;
+    } else if (cbor_isa_bytestring(value)) {
+        str = cbor_bytestring_handle(value);
+        if (!str) {
+            goto out;
+        }
+        prop->len = cbor_bytestring_length(value);
+    } else if (cbor_isa_string(value)) {
+        str = cbor_string_handle(value);
+        if (!str) {
+            goto out;
+        }
+        prop->len = cbor_string_length(value);
+    } else {
+        goto out;
+    }
+
+    /*
+     * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
+     * already check for the max request size before processing
+     * any request. So it's safe to copy.
+     */
+    memcpy(prop->buf, str, prop->len);
+    prop->is_null = false;
+    ret = true;
+
+ out:
+    return ret;
+}
+
+static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
+                                                     NSMAttestationReq *nsm_req)
+{
+    cbor_item_t *item = NULL;
+    size_t size;
+    uint8_t *str;
+    struct cbor_pair *pair;
+    struct cbor_load_result result;
+    enum NSMResponseTypes r = NSM_INVALID_OPERATION;
+
+    nsm_req->public_key.is_null = true;
+    nsm_req->user_data.is_null = true;
+    nsm_req->nonce.is_null = true;
+
+    item = cbor_load(req, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(item);
+    if (!cbor_isa_map(pair->value)) {
+        goto cleanup;
+    }
+    size = cbor_map_size(pair->value);
+    if (size == 0) {
+        r = NSM_SUCCESS;
+        goto cleanup;
+    }
+
+    pair = cbor_map_handle(pair->value);
+    for (int i = 0; i < size; ++i) {
+        if (!cbor_isa_string(pair[i].key)) {
+            continue;
+        }
+
+        str = cbor_string_handle(pair[i].key);
+        if (!str) {
+            continue;
+        }
+
+        if (cbor_string_length(pair[i].key) == 10 &&
+            memcmp(str, "public_key", 10) == 0) {
+            if (!fill_attestation_property(&(nsm_req->public_key),
+                                           pair[i].value)) {
+                goto cleanup;
+            }
+            continue;
+        }
+
+        if (cbor_string_length(pair[i].key) == 9 &&
+            memcmp(str, "user_data", 9) == 0) {
+            if (!fill_attestation_property(&(nsm_req->user_data),
+                                           pair[i].value)) {
+                goto cleanup;
+            }
+            continue;
+        }
+
+        if (cbor_string_length(pair[i].key) == 5 &&
+            memcmp(str, "nonce", 5) == 0) {
+            if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
+                goto cleanup;
+            }
+            continue;
+        }
+    }
+
+    r = NSM_SUCCESS;
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return r;
+}
+
+static bool add_protected_header_to_cose(cbor_item_t *cose)
+{
+    cbor_item_t *map = NULL;
+    cbor_item_t *key = NULL;
+    cbor_item_t *value = NULL;
+    cbor_item_t *bs = NULL;
+    size_t len;
+    bool r = false;
+    size_t buf_len = 4096;
+    g_autofree uint8_t *buf = g_malloc(buf_len);
+
+    map = cbor_new_definite_map(1);
+    if (!map) {
+        goto cleanup;
+    }
+    key = cbor_build_uint8(1);
+    if (!key) {
+        goto cleanup;
+    }
+    value = cbor_new_int8();
+    if (!value) {
+        goto cleanup;
+    }
+    cbor_mark_negint(value);
+    /* we don't actually sign the data, so we use -1 as the 'alg' value */
+    cbor_set_uint8(value, 0);
+
+    if (!qemu_cbor_map_add(map, key, value)) {
+        goto cleanup;
+    }
+
+    len = cbor_serialize(map, buf, buf_len);
+    if (len == 0) {
+        goto cleanup_map;
+    }
+
+    bs = cbor_build_bytestring(buf, len);
+    if (!bs) {
+        goto cleanup_map;
+    }
+    if (!qemu_cbor_array_push(cose, bs)) {
+        cbor_decref(&bs);
+        goto cleanup_map;
+    }
+    r = true;
+    goto cleanup_map;
+
+ cleanup:
+    if (key) {
+        cbor_decref(&key);
+    }
+    if (value) {
+        cbor_decref(&value);
+    }
+
+ cleanup_map:
+    if (map) {
+        cbor_decref(&map);
+    }
+    return r;
+}
+
+static bool add_unprotected_header_to_cose(cbor_item_t *cose)
+{
+    cbor_item_t *map = cbor_new_definite_map(0);
+    if (!map) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_array_push(cose, map)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (map) {
+        cbor_decref(&map);
+    }
+    return false;
+}
+
+static bool add_ca_bundle_to_payload(cbor_item_t *map)
+{
+    cbor_item_t *key_cbor = NULL;
+    cbor_item_t *value_cbor = NULL;
+    cbor_item_t *bs = NULL;
+    uint8_t zero[64] = {0};
+
+    key_cbor = cbor_build_string("cabundle");
+    if (!key_cbor) {
+        goto cleanup;
+    }
+    value_cbor = cbor_new_definite_array(1);
+    if (!value_cbor) {
+        goto cleanup;
+    }
+    bs = cbor_build_bytestring(zero, 64);
+    if (!bs) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_array_push(value_cbor, bs)) {
+        cbor_decref(&bs);
+        goto cleanup;
+    }
+    if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (key_cbor) {
+        cbor_decref(&key_cbor);
+    }
+    if (value_cbor) {
+        cbor_decref(&value_cbor);
+    }
+    return false;
+}
+
+static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
+                                NSMAttestationReq *req)
+{
+    cbor_item_t *root = NULL;
+    cbor_item_t *nested_map;
+    cbor_item_t *bs = NULL;
+    size_t locked_cnt;
+    uint8_t ind[NSM_MAX_PCRS];
+    size_t payload_map_size = 9;
+    size_t len;
+    struct PCRInfo *pcr;
+    uint8_t zero[64] = {0};
+    bool r = false;
+    size_t buf_len = 16384;
+    g_autofree uint8_t *buf = g_malloc(buf_len);
+
+    root = cbor_new_definite_map(payload_map_size);
+    if (!root) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
+                                     (uint64_t) time(NULL) * 1000)) {
+        goto cleanup;
+    }
+
+    locked_cnt = 0;
+    for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
+        if (vnsm->pcrs[i].locked) {
+            ind[locked_cnt++] = i;
+        }
+    }
+    if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
+        goto cleanup;
+    }
+    for (uint8_t i = 0; i < locked_cnt; ++i) {
+        pcr = &(vnsm->pcrs[ind[i]]);
+        if (!qemu_cbor_add_uint8_key_bytestring_to_map(
+                nested_map, ind[i],
+                pcr->data,
+                QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
+            goto cleanup;
+        }
+    }
+    if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
+        goto cleanup;
+    }
+    if (!add_ca_bundle_to_payload(root)) {
+        goto cleanup;
+    }
+
+    if (req->public_key.is_null) {
+        if (!qemu_cbor_add_null_to_map(root, "public_key")) {
+            goto cleanup;
+        }
+    } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
+                                                req->public_key.buf,
+                                                req->public_key.len)) {
+        goto cleanup;
+    }
+
+    if (req->user_data.is_null) {
+        if (!qemu_cbor_add_null_to_map(root, "user_data")) {
+            goto cleanup;
+        }
+    } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
+                                                req->user_data.buf,
+                                                req->user_data.len)) {
+            goto cleanup;
+    }
+
+    if (req->nonce.is_null) {
+        if (!qemu_cbor_add_null_to_map(root, "nonce")) {
+            goto cleanup;
+        }
+    } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
+                                                req->nonce.buf,
+                                                req->nonce.len)) {
+        goto cleanup;
+    }
+
+    len = cbor_serialize(root, buf, buf_len);
+    if (len == 0) {
+        goto cleanup;
+    }
+
+    bs = cbor_build_bytestring(buf, len);
+    if (!bs) {
+        goto cleanup;
+    }
+    if (!qemu_cbor_array_push(cose, bs)) {
+        cbor_decref(&bs);
+        goto cleanup;
+    }
+
+    r = true;
+
+ cleanup:
+    if (root) {
+        cbor_decref(&root);
+    }
+    return r;
+}
+
+static bool add_signature_to_cose(cbor_item_t *cose)
+{
+    cbor_item_t *bs = NULL;
+    uint8_t zero[64] = {0};
+
+    /* we don't actually sign the data, so we just put 64 zero bytes */
+    bs = cbor_build_bytestring(zero, 64);
+    if (!bs) {
+        goto cleanup;
+    }
+
+    if (!qemu_cbor_array_push(cose, bs)) {
+        goto cleanup;
+    }
+
+    return true;
+
+ cleanup:
+    if (bs) {
+        cbor_decref(&bs);
+    }
+    return false;
+}
+
+/*
+ * Attestation response structure:
+ *
+ * {
+ *   Map(1) {
+ *     key = String("Attestation"),
+ *     value = Map(1) {
+ *       key = String("document"),
+ *       value = Byte_String()
+ *     }
+ *   }
+ * }
+ *
+ * The document is a serialized COSE sign1 blob of the structure:
+ * {
+ *   Array(4) {
+ *     [0] { ByteString() }, // serialized protected header
+ *     [1] { Map(0) },       // 0 length map
+ *     [2] { ByteString() }, // serialized payload
+ *     [3] { ByteString() }, // signature
+ *   }
+ * }
+ *
+ * where [0] protected header is a serialized CBOR blob of the structure:
+ * {
+ *   Map(1) {
+ *     key = Uint8(1)         // alg
+ *     value = NegativeInt8() // Signing algorithm
+ *   }
+ * }
+ *
+ * [2] payload is serialized CBOR blob of the structure:
+ * {
+ *   Map(9) {
+ *     [0] { key = String("module_id"), value = String(module_id) },
+ *     [1] { key = String("digest"), value = String("SHA384") },
+ *     [2] {
+ *           key = String("timestamp"),
+ *           value = Uint64(unix epoch of  when document was created)
+ *         },
+ *     [3] {
+ *           key = String("pcrs"),
+ *           value = Map(locked_pcr_cnt) {
+ *                       key = Uint8(pcr_index),
+ *                       value = ByteString(pcr_data)
+ *                   },
+ *         },
+ *     [4] {
+ *           key = String("certificate"),
+ *           value = ByteString(Signing certificate)
+ *         },
+ *     [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
+ *     [6] { key = String("public_key"), value = ByteString() || null },
+ *     [7] { key = String("user_data"), value = ByteString() || null},
+ *     [8] { key = String("nonce"), value = ByteString() || null},
+ *   }
+ * }
+ */
+static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
+                               struct iovec *response, Error **errp)
+{
+    cbor_item_t *root = NULL;
+    cbor_item_t *cose = NULL;
+    cbor_item_t *nested_map;
+    size_t len;
+    enum NSMResponseTypes type;
+    bool r = false;
+    size_t buf_len = 16384;
+    g_autofree uint8_t *buf = g_malloc(buf_len);
+    g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
+
+    nsm_req->public_key.is_null = true;
+    nsm_req->user_data.is_null = true;
+    nsm_req->nonce.is_null = true;
+
+    type = get_nsm_attestation_req(request->iov_base, request->iov_len,
+                                   nsm_req);
+    if (type != NSM_SUCCESS) {
+        if (error_response(response, type, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    cose = cbor_new_definite_array(4);
+    if (!cose) {
+        goto err;
+    }
+    if (!add_protected_header_to_cose(cose)) {
+        goto err;
+    }
+    if (!add_unprotected_header_to_cose(cose)) {
+        goto err;
+    }
+    if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
+        goto err;
+    }
+    if (!add_signature_to_cose(cose)) {
+        goto err;
+    }
+
+    len = cbor_serialize(cose, buf, buf_len);
+    if (len == 0) {
+        goto err;
+    }
+
+    root = cbor_new_definite_map(1);
+    if (!root) {
+        goto err;
+    }
+    if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
+        goto err;
+    }
+    if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
+        goto err;
+    }
+
+    len = cbor_serialize(root, response->iov_base, response->iov_len);
+    if (len == 0) {
+        if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
+            r = true;
+        }
+        goto out;
+    }
+
+    response->iov_len = len;
+    r = true;
+
+ out:
+    if (root) {
+        cbor_decref(&root);
+    }
+    if (cose) {
+        cbor_decref(&cose);
+    }
+    return r;
+
+ err:
+    error_setg(errp, "Failed to initialize Attestation response");
+    goto out;
+}
+
+enum CBOR_ROOT_TYPE {
+    CBOR_ROOT_TYPE_STRING = 0,
+    CBOR_ROOT_TYPE_MAP = 1,
+};
+
+struct nsm_cmd {
+    char name[16];
+    /*
+     * There are 2 types of request
+     * 1) String(); "GetRandom", "DescribeNSM"
+     * 2) Map(1) { key: String(), value: ... }
+     */
+    enum CBOR_ROOT_TYPE root_type;
+    bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
+                        struct iovec *response, Error **errp);
+};
+
+const struct nsm_cmd nsm_cmds[] = {
+    { "GetRandom",   CBOR_ROOT_TYPE_STRING,  handle_get_random },
+    { "DescribeNSM", CBOR_ROOT_TYPE_STRING,  handle_describe_nsm },
+    { "DescribePCR", CBOR_ROOT_TYPE_MAP,     handle_describe_pcr },
+    { "ExtendPCR",   CBOR_ROOT_TYPE_MAP,     handle_extend_pcr },
+    { "LockPCR",     CBOR_ROOT_TYPE_MAP,     handle_lock_pcr },
+    { "LockPCRs",    CBOR_ROOT_TYPE_MAP,     handle_lock_pcrs },
+    { "Attestation", CBOR_ROOT_TYPE_MAP,     handle_attestation },
+};
+
+static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
+{
+    size_t size;
+    uint8_t *req;
+    enum CBOR_ROOT_TYPE root_type;
+    struct cbor_load_result result;
+    cbor_item_t *item = cbor_load(buf, len, &result);
+    if (!item || result.error.code != CBOR_ERR_NONE) {
+        goto cleanup;
+    }
+
+    if (cbor_isa_string(item)) {
+        size = cbor_string_length(item);
+        req = cbor_string_handle(item);
+        root_type = CBOR_ROOT_TYPE_STRING;
+    } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
+        struct cbor_pair *handle = cbor_map_handle(item);
+        if (cbor_isa_string(handle->key)) {
+            size = cbor_string_length(handle->key);
+            req = cbor_string_handle(handle->key);
+            root_type = CBOR_ROOT_TYPE_MAP;
+        } else {
+            goto cleanup;
+        }
+    } else {
+        goto cleanup;
+    }
+
+    if  (size == 0 || req == NULL) {
+        goto cleanup;
+    }
+
+    for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
+        if (nsm_cmds[i].root_type == root_type &&
+            strlen(nsm_cmds[i].name) == size &&
+            memcmp(nsm_cmds[i].name, req, size) == 0) {
+            cbor_decref(&item);
+            return &nsm_cmds[i];
+        }
+    }
+
+ cleanup:
+    if (item) {
+        cbor_decref(&item);
+    }
+    return NULL;
+}
+
+static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
+                                     struct iovec *resp, Error **errp)
+{
+    const struct nsm_cmd *cmd;
+
+    if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
+        if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
+            return true;
+        }
+        error_setg(errp, "Failed to initialize InputTooLarge response");
+        return false;
+    }
+
+    cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
+
+    if (cmd == NULL) {
+        if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
+            return true;
+        }
+        error_setg(errp, "Failed to initialize InvalidOperation response");
+        return false;
+    }
+
+    return cmd->response_fn(vnsm, req, resp, errp);
+}
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    g_autofree VirtQueueElement *out_elem = NULL;
+    g_autofree VirtQueueElement *in_elem = NULL;
+    VirtIONSM *vnsm = VIRTIO_NSM(vdev);
+    Error *err = NULL;
+    size_t sz;
+    struct iovec req = {.iov_base = NULL, .iov_len = 0};
+    struct iovec res = {.iov_base = NULL, .iov_len = 0};
+
+    out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+    if (!out_elem) {
+        /* nothing in virtqueue */
+        return;
+    }
+
+    sz = iov_size(out_elem->out_sg, out_elem->out_num);
+    if (sz == 0) {
+        virtio_error(vdev, "Expected non-zero sized request buffer in "
+                     "virtqueue");
+        goto cleanup;
+    }
+
+    in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
+    if (!in_elem) {
+        virtio_error(vdev, "Expected response buffer after request buffer "
+                     "in virtqueue");
+        goto cleanup;
+    }
+    if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
+        virtio_error(vdev, "Expected response buffer of length 0x3000");
+        goto cleanup;
+    }
+
+    req.iov_base = g_malloc(sz);
+    req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
+                             req.iov_base, sz);
+    if (req.iov_len != sz) {
+        virtio_error(vdev, "Failed to copy request buffer");
+        goto cleanup;
+    }
+
+    res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
+    res.iov_len = NSM_RESPONSE_BUF_SIZE;
+
+    if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
+        error_report_err(err);
+        virtio_error(vdev, "Failed to get NSM request response");
+        goto cleanup;
+    }
+
+    sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
+                      res.iov_len);
+    if (sz != res.iov_len) {
+        virtio_error(vdev, "Failed to copy response buffer");
+        goto cleanup;
+    }
+
+    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);
+    virtio_notify(vdev, vq);
+    return;
+
+ cleanup:
+    g_free(req.iov_base);
+    g_free(res.iov_base);
+    if (out_elem) {
+        virtqueue_detach_element(vq, out_elem, 0);
+    }
+    if (in_elem) {
+        virtqueue_detach_element(vq, in_elem, 0);
+    }
+    return;
+}
+
+static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
+{
+    return f;
+}
+
+static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
+{
+    Error *err = NULL;
+    struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
+    size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
+    uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
+    uint8_t *ptr = result;
+    struct iovec iov[2] = {
+        { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
+        { .iov_base = data, .iov_len = len },
+    };
+
+    if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
+                            &err) < 0) {
+        return false;
+    }
+
+    memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
+    return true;
+}
+
+static void lock_pcr(VirtIONSM *vnsm, int ind)
+{
+    vnsm->pcrs[ind].locked = true;
+}
+
+static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    VirtIONSM *vnsm = VIRTIO_NSM(dev);
+
+    vnsm->max_pcrs = NSM_MAX_PCRS;
+    vnsm->digest = (char *) "SHA384";
+    if (vnsm->module_id == NULL) {
+        vnsm->module_id = (char *) "i-234-enc5678";
+    }
+    vnsm->version_major = 1;
+    vnsm->version_minor = 0;
+    vnsm->version_patch = 0;
+    vnsm->extend_pcr = extend_pcr;
+    vnsm->lock_pcr = lock_pcr;
+
+    virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
+
+    vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
+}
+
+static void virtio_nsm_device_unrealize(DeviceState *dev)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+
+    virtio_del_queue(vdev, 0);
+    virtio_cleanup(vdev);
+}
+
+static const VMStateDescription vmstate_pcr_info_entry = {
+    .name = "pcr_info_entry",
+    .minimum_version_id = 1,
+    .version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BOOL(locked, struct PCRInfo),
+        VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
+                            QCRYPTO_HASH_DIGEST_LEN_SHA384),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static const VMStateDescription vmstate_virtio_nsm_device = {
+    .name = "virtio-nsm-device",
+    .minimum_version_id = 1,
+    .version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
+                             vmstate_pcr_info_entry, struct PCRInfo),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static const VMStateDescription vmstate_virtio_nsm = {
+    .name = "virtio-nsm",
+    .minimum_version_id = 1,
+    .version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_VIRTIO_DEVICE,
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static 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)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+    device_class_set_props(dc, virtio_nsm_properties);
+    dc->vmsd = &vmstate_virtio_nsm;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    vdc->realize = virtio_nsm_device_realize;
+    vdc->unrealize = virtio_nsm_device_unrealize;
+    vdc->get_features = get_features;
+    vdc->vmsd = &vmstate_virtio_nsm_device;
+}
+
+static const TypeInfo virtio_nsm_info = {
+    .name = TYPE_VIRTIO_NSM,
+    .parent = TYPE_VIRTIO_DEVICE,
+    .instance_size = sizeof(VirtIONSM),
+    .class_init = virtio_nsm_class_init,
+};
+
+static void virtio_register_types(void)
+{
+    type_register_static(&virtio_nsm_info);
+}
+
+type_init(virtio_register_types)

+ 2 - 0
include/hw/boards.h

@@ -314,6 +314,8 @@ struct MachineClass {
     int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
     int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
     ram_addr_t (*fixup_ram_size)(ram_addr_t size);
     ram_addr_t (*fixup_ram_size)(ram_addr_t size);
     uint64_t smbios_memory_device_size;
     uint64_t smbios_memory_device_size;
+    bool (*create_default_memdev)(MachineState *ms, const char *path,
+                                  Error **errp);
 };
 };
 
 
 /**
 /**

+ 2 - 0
include/hw/i386/microvm.h

@@ -78,6 +78,8 @@ struct MicrovmMachineClass {
     X86MachineClass parent;
     X86MachineClass parent;
     HotplugHandler *(*orig_hotplug_handler)(MachineState *machine,
     HotplugHandler *(*orig_hotplug_handler)(MachineState *machine,
                                            DeviceState *dev);
                                            DeviceState *dev);
+    void (*x86_load_linux)(X86MachineState *x86ms, FWCfgState *fw_cfg,
+                        int acpi_data_size, bool pvh_enabled);
 };
 };
 
 
 struct MicrovmMachineState {
 struct MicrovmMachineState {

+ 62 - 0
include/hw/i386/nitro_enclave.h

@@ -0,0 +1,62 @@
+/*
+ * AWS nitro-enclave machine
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef HW_I386_NITRO_ENCLAVE_H
+#define HW_I386_NITRO_ENCLAVE_H
+
+#include "crypto/hash.h"
+#include "hw/i386/microvm.h"
+#include "qom/object.h"
+#include "hw/virtio/virtio-nsm.h"
+
+/* Machine type options */
+#define NITRO_ENCLAVE_VSOCK_CHARDEV_ID "vsock"
+#define NITRO_ENCLAVE_ID    "id"
+#define NITRO_ENCLAVE_PARENT_ROLE "parent-role"
+#define NITRO_ENCLAVE_PARENT_ID "parent-id"
+
+struct NitroEnclaveMachineClass {
+    MicrovmMachineClass parent;
+
+    void (*parent_init)(MachineState *state);
+    void (*parent_reset)(MachineState *machine, ResetType type);
+};
+
+struct NitroEnclaveMachineState {
+    MicrovmMachineState parent;
+
+    /* Machine type options */
+    char *vsock;
+    /* Enclave identifier */
+    char *id;
+    /* Parent instance IAM role ARN */
+    char *parent_role;
+    /* Parent instance identifier */
+    char *parent_id;
+
+    /* 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];
+    bool signature_found;
+};
+
+#define TYPE_NITRO_ENCLAVE_MACHINE MACHINE_TYPE_NAME("nitro-enclave")
+OBJECT_DECLARE_TYPE(NitroEnclaveMachineState, NitroEnclaveMachineClass,
+                    NITRO_ENCLAVE_MACHINE)
+
+#endif

+ 45 - 0
include/hw/virtio/cbor-helpers.h

@@ -0,0 +1,45 @@
+/*
+ * QEMU CBOR helpers
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef QEMU_VIRTIO_CBOR_HELPERS_H
+#define QEMU_VIRTIO_CBOR_HELPERS_H
+
+#include <cbor.h>
+
+bool qemu_cbor_map_add(cbor_item_t *map, cbor_item_t *key, cbor_item_t *value);
+
+bool qemu_cbor_array_push(cbor_item_t *array, cbor_item_t *value);
+
+bool qemu_cbor_add_bool_to_map(cbor_item_t *map, const char *key, bool value);
+
+bool qemu_cbor_add_uint8_to_map(cbor_item_t *map, const char *key,
+                                uint8_t value);
+
+bool qemu_cbor_add_map_to_map(cbor_item_t *map, const char *key,
+                              size_t nested_map_size,
+                              cbor_item_t **nested_map);
+
+bool qemu_cbor_add_bytestring_to_map(cbor_item_t *map, const char *key,
+                                     uint8_t *arr, size_t len);
+
+bool qemu_cbor_add_null_to_map(cbor_item_t *map, const char *key);
+
+bool qemu_cbor_add_string_to_map(cbor_item_t *map, const char *key,
+                                 const char *value);
+
+bool qemu_cbor_add_uint8_array_to_map(cbor_item_t *map, const char *key,
+                                      uint8_t *arr, size_t len);
+
+bool qemu_cbor_add_uint8_key_bytestring_to_map(cbor_item_t *map, uint8_t key,
+                                               uint8_t *buf, size_t len);
+
+bool qemu_cbor_add_uint64_to_map(cbor_item_t *map, const char *key,
+                                 uint64_t value);
+#endif

+ 49 - 0
include/hw/virtio/virtio-nsm.h

@@ -0,0 +1,49 @@
+/*
+ * AWS Nitro Secure Module (NSM) device
+ *
+ * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef QEMU_VIRTIO_NSM_H
+#define QEMU_VIRTIO_NSM_H
+
+#include "crypto/hash.h"
+#include "hw/virtio/virtio.h"
+#include "qom/object.h"
+
+#define NSM_MAX_PCRS 32
+
+#define TYPE_VIRTIO_NSM "virtio-nsm-device"
+OBJECT_DECLARE_SIMPLE_TYPE(VirtIONSM, VIRTIO_NSM)
+#define VIRTIO_NSM_GET_PARENT_CLASS(obj) \
+    OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_NSM)
+
+struct PCRInfo {
+    bool locked;
+    uint8_t data[QCRYPTO_HASH_DIGEST_LEN_SHA384];
+};
+
+struct VirtIONSM {
+    VirtIODevice parent_obj;
+
+    /* Only one vq - guest puts request and response buffers on it */
+    VirtQueue *vq;
+
+    /* NSM State */
+    uint16_t max_pcrs;
+    struct PCRInfo pcrs[NSM_MAX_PCRS];
+    char *digest;
+    char *module_id;
+    uint8_t version_major;
+    uint8_t version_minor;
+    uint8_t version_patch;
+
+    bool (*extend_pcr)(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len);
+    void (*lock_pcr)(VirtIONSM *vnsm, int ind);
+};
+
+#endif

+ 9 - 0
include/qemu/host-utils.h

@@ -313,6 +313,15 @@ static inline int ctpop8(uint8_t val)
     return __builtin_popcount(val);
     return __builtin_popcount(val);
 }
 }
 
 
+/*
+ * parity8 - return the parity (1 = odd) of an 8-bit value.
+ * @val: The value to search
+ */
+static inline int parity8(uint8_t val)
+{
+    return __builtin_parity(val);
+}
+
 /**
 /**
  * ctpop16 - count the population of one bits in a 16-bit value.
  * ctpop16 - count the population of one bits in a 16-bit value.
  * @val: The value to search
  * @val: The value to search

+ 0 - 8
include/qom/object.h

@@ -2032,14 +2032,6 @@ int object_child_foreach_recursive(Object *obj,
  */
  */
 Object *container_get(Object *root, const char *path);
 Object *container_get(Object *root, const char *path);
 
 
-/**
- * object_type_get_instance_size:
- * @typename: Name of the Type whose instance_size is required
- *
- * Returns the instance_size of the given @typename.
- */
-size_t object_type_get_instance_size(const char *typename);
-
 /**
 /**
  * object_property_help:
  * object_property_help:
  * @name: the name of the property
  * @name: the name of the property

+ 2 - 0
include/sysemu/hostmem.h

@@ -39,6 +39,8 @@ OBJECT_DECLARE_TYPE(HostMemoryBackend, HostMemoryBackendClass,
  */
  */
 #define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
 #define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
 
 
+#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
+
 
 
 /**
 /**
  * HostMemoryBackendClass:
  * HostMemoryBackendClass:

+ 17 - 0
meson.build

@@ -1716,6 +1716,12 @@ if (have_system or have_tools) and (virgl.found() or opengl.found())
 endif
 endif
 have_vhost_user_gpu = have_vhost_user_gpu and virgl.found() and opengl.found() and gbm.found()
 have_vhost_user_gpu = have_vhost_user_gpu and virgl.found() and opengl.found() and gbm.found()
 
 
+libcbor = not_found
+if not get_option('libcbor').auto() or have_system
+  libcbor = dependency('libcbor', version: '>=0.7.0',
+                       required: get_option('libcbor'))
+endif
+
 gnutls = not_found
 gnutls = not_found
 gnutls_crypto = not_found
 gnutls_crypto = not_found
 if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system)
 if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system)
@@ -3127,6 +3133,8 @@ host_kconfig = \
   (spice.found() ? ['CONFIG_SPICE=y'] : []) + \
   (spice.found() ? ['CONFIG_SPICE=y'] : []) + \
   (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \
   (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \
   (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \
   (opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \
+  (libcbor.found() ? ['CONFIG_LIBCBOR=y'] : []) + \
+  (gnutls.found() ? ['CONFIG_GNUTLS=y'] : []) + \
   (x11.found() ? ['CONFIG_X11=y'] : []) + \
   (x11.found() ? ['CONFIG_X11=y'] : []) + \
   (fdt.found() ? ['CONFIG_FDT=y'] : []) + \
   (fdt.found() ? ['CONFIG_FDT=y'] : []) + \
   (have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \
   (have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \
@@ -4694,6 +4702,7 @@ summary_info += {'NUMA host support': numa}
 summary_info += {'capstone':          capstone}
 summary_info += {'capstone':          capstone}
 summary_info += {'libpmem support':   libpmem}
 summary_info += {'libpmem support':   libpmem}
 summary_info += {'libdaxctl support': libdaxctl}
 summary_info += {'libdaxctl support': libdaxctl}
+summary_info += {'libcbor support':   libcbor}
 summary_info += {'libudev':           libudev}
 summary_info += {'libudev':           libudev}
 # Dummy dependency, keep .found()
 # Dummy dependency, keep .found()
 summary_info += {'FUSE lseek':        fuse_lseek.found()}
 summary_info += {'FUSE lseek':        fuse_lseek.found()}
@@ -4718,6 +4727,14 @@ if host_arch == 'unknown'
     message('configure has succeeded and you can continue to build, but')
     message('configure has succeeded and you can continue to build, but')
     message('QEMU will use a slow interpreter to emulate the target CPU.')
     message('QEMU will use a slow interpreter to emulate the target CPU.')
   endif
   endif
+elif host_arch == 'mips'
+  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.')
 endif
 endif
 
 
 if not supported_oses.contains(host_os)
 if not supported_oses.contains(host_os)

+ 2 - 0
meson_options.txt

@@ -168,6 +168,8 @@ option('iconv', type : 'feature', value : 'auto',
        description: 'Font glyph conversion support')
        description: 'Font glyph conversion support')
 option('curses', type : 'feature', value : 'auto',
 option('curses', type : 'feature', value : 'auto',
        description: 'curses UI')
        description: 'curses UI')
+option('libcbor', type : 'feature', value : 'auto',
+       description: 'libcbor support')
 option('gnutls', type : 'feature', value : 'auto',
 option('gnutls', type : 'feature', value : 'auto',
        description: 'GNUTLS cryptography support')
        description: 'GNUTLS cryptography support')
 option('nettle', type : 'feature', value : 'auto',
 option('nettle', type : 'feature', value : 'auto',

+ 36 - 44
qom/object.c

@@ -195,7 +195,7 @@ void type_register_static_array(const TypeInfo *infos, int nr_infos)
     }
     }
 }
 }
 
 
-static TypeImpl *type_get_by_name(const char *name)
+static TypeImpl *type_get_by_name_noload(const char *name)
 {
 {
     if (name == NULL) {
     if (name == NULL) {
         return NULL;
         return NULL;
@@ -204,10 +204,32 @@ static TypeImpl *type_get_by_name(const char *name)
     return type_table_lookup(name);
     return type_table_lookup(name);
 }
 }
 
 
+static TypeImpl *type_get_or_load_by_name(const char *name, Error **errp)
+{
+    TypeImpl *type = type_get_by_name_noload(name);
+
+#ifdef CONFIG_MODULES
+    if (!type) {
+        int rv = module_load_qom(name, errp);
+        if (rv > 0) {
+            type = type_get_by_name_noload(name);
+        } else {
+            error_prepend(errp, "could not load a module for type '%s'", name);
+            return NULL;
+        }
+    }
+#endif
+    if (!type) {
+        error_setg(errp, "unknown type '%s'", name);
+    }
+
+    return type;
+}
+
 static TypeImpl *type_get_parent(TypeImpl *type)
 static TypeImpl *type_get_parent(TypeImpl *type)
 {
 {
     if (!type->parent_type && type->parent) {
     if (!type->parent_type && type->parent) {
-        type->parent_type = type_get_by_name(type->parent);
+        type->parent_type = type_get_by_name_noload(type->parent);
         if (!type->parent_type) {
         if (!type->parent_type) {
             fprintf(stderr, "Type '%s' is missing its parent '%s'\n",
             fprintf(stderr, "Type '%s' is missing its parent '%s'\n",
                     type->name, type->parent);
                     type->name, type->parent);
@@ -262,14 +284,6 @@ static size_t type_object_get_align(TypeImpl *ti)
     return 0;
     return 0;
 }
 }
 
 
-size_t object_type_get_instance_size(const char *typename)
-{
-    TypeImpl *type = type_get_by_name(typename);
-
-    g_assert(type != NULL);
-    return type_object_get_size(type);
-}
-
 static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type)
 static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type)
 {
 {
     assert(target_type);
     assert(target_type);
@@ -371,7 +385,7 @@ static void type_initialize(TypeImpl *ti)
         }
         }
 
 
         for (i = 0; i < ti->num_interfaces; i++) {
         for (i = 0; i < ti->num_interfaces; i++) {
-            TypeImpl *t = type_get_by_name(ti->interfaces[i].typename);
+            TypeImpl *t = type_get_by_name_noload(ti->interfaces[i].typename);
             if (!t) {
             if (!t) {
                 error_report("missing interface '%s' for object '%s'",
                 error_report("missing interface '%s' for object '%s'",
                              ti->interfaces[i].typename, parent->name);
                              ti->interfaces[i].typename, parent->name);
@@ -565,23 +579,7 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
 
 
 void object_initialize(void *data, size_t size, const char *typename)
 void object_initialize(void *data, size_t size, const char *typename)
 {
 {
-    TypeImpl *type = type_get_by_name(typename);
-
-#ifdef CONFIG_MODULES
-    if (!type) {
-        int rv = module_load_qom(typename, &error_fatal);
-        if (rv > 0) {
-            type = type_get_by_name(typename);
-        } else {
-            error_report("missing object type '%s'", typename);
-            exit(1);
-        }
-    }
-#endif
-    if (!type) {
-        error_report("missing object type '%s'", typename);
-        abort();
-    }
+    TypeImpl *type = type_get_or_load_by_name(typename, &error_fatal);
 
 
     object_initialize_with_type(data, size, type);
     object_initialize_with_type(data, size, type);
 }
 }
@@ -792,7 +790,7 @@ Object *object_new_with_class(ObjectClass *klass)
 
 
 Object *object_new(const char *typename)
 Object *object_new(const char *typename)
 {
 {
-    TypeImpl *ti = type_get_by_name(typename);
+    TypeImpl *ti = type_get_or_load_by_name(typename, &error_fatal);
 
 
     return object_new_with_type(ti);
     return object_new_with_type(ti);
 }
 }
@@ -965,7 +963,7 @@ ObjectClass *object_class_dynamic_cast(ObjectClass *class,
         return class;
         return class;
     }
     }
 
 
-    target_type = type_get_by_name(typename);
+    target_type = type_get_by_name_noload(typename);
     if (!target_type) {
     if (!target_type) {
         /* target class type unknown, so fail the cast */
         /* target class type unknown, so fail the cast */
         return NULL;
         return NULL;
@@ -1063,7 +1061,7 @@ const char *object_class_get_name(ObjectClass *klass)
 
 
 ObjectClass *object_class_by_name(const char *typename)
 ObjectClass *object_class_by_name(const char *typename)
 {
 {
-    TypeImpl *type = type_get_by_name(typename);
+    TypeImpl *type = type_get_by_name_noload(typename);
 
 
     if (!type) {
     if (!type) {
         return NULL;
         return NULL;
@@ -1076,21 +1074,15 @@ ObjectClass *object_class_by_name(const char *typename)
 
 
 ObjectClass *module_object_class_by_name(const char *typename)
 ObjectClass *module_object_class_by_name(const char *typename)
 {
 {
-    ObjectClass *oc;
+    TypeImpl *type = type_get_or_load_by_name(typename, NULL);
 
 
-    oc = object_class_by_name(typename);
-#ifdef CONFIG_MODULES
-    if (!oc) {
-        Error *local_err = NULL;
-        int rv = module_load_qom(typename, &local_err);
-        if (rv > 0) {
-            oc = object_class_by_name(typename);
-        } else if (rv < 0) {
-            error_report_err(local_err);
-        }
+    if (!type) {
+        return NULL;
     }
     }
-#endif
-    return oc;
+
+    type_initialize(type);
+
+    return type->class;
 }
 }
 
 
 ObjectClass *object_class_get_parent(ObjectClass *class)
 ObjectClass *object_class_get_parent(ObjectClass *class)

+ 2 - 2
qom/object_interfaces.c

@@ -90,7 +90,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
         return NULL;
         return NULL;
     }
     }
 
 
-    klass = object_class_by_name(type);
+    klass = module_object_class_by_name(type);
     if (!klass) {
     if (!klass) {
         error_setg(errp, "invalid object type: %s", type);
         error_setg(errp, "invalid object type: %s", type);
         return NULL;
         return NULL;
@@ -108,7 +108,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
     }
     }
 
 
     assert(qdict);
     assert(qdict);
-    obj = object_new(type);
+    obj = object_new_with_class(klass);
     object_set_properties_from_qdict(obj, qdict, v, &local_err);
     object_set_properties_from_qdict(obj, qdict, v, &local_err);
     if (local_err) {
     if (local_err) {
         goto out;
         goto out;

+ 2 - 2
qom/qom-qmp-cmds.c

@@ -141,7 +141,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
         return NULL;
         return NULL;
     }
     }
 
 
-    obj = object_new(typename);
+    obj = object_new_with_class(klass);
 
 
     object_property_iter_init(&iter, obj);
     object_property_iter_init(&iter, obj);
     while ((prop = object_property_iter_next(&iter))) {
     while ((prop = object_property_iter_next(&iter))) {
@@ -186,7 +186,7 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
     ObjectPropertyIterator iter;
     ObjectPropertyIterator iter;
     ObjectPropertyInfoList *prop_list = NULL;
     ObjectPropertyInfoList *prop_list = NULL;
 
 
-    klass = object_class_by_name(typename);
+    klass = module_object_class_by_name(typename);
     if (klass == NULL) {
     if (klass == NULL) {
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
         error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                   "Class '%s' not found", typename);
                   "Class '%s' not found", typename);

+ 3 - 0
scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml

@@ -7,6 +7,7 @@
 packages:
 packages:
   - bash
   - bash
   - bc
   - bc
+  - bindgen
   - bison
   - bison
   - bsdextrautils
   - bsdextrautils
   - bzip2
   - bzip2
@@ -35,6 +36,7 @@ packages:
   - libcacard-dev
   - libcacard-dev
   - libcap-ng-dev
   - libcap-ng-dev
   - libcapstone-dev
   - libcapstone-dev
+  - libcbor-dev
   - libcmocka-dev
   - libcmocka-dev
   - libcurl4-gnutls-dev
   - libcurl4-gnutls-dev
   - libdaxctl-dev
   - libdaxctl-dev
@@ -113,6 +115,7 @@ packages:
   - python3-venv
   - python3-venv
   - python3-yaml
   - python3-yaml
   - rpm2cpio
   - rpm2cpio
+  - rustc
   - sed
   - sed
   - socat
   - socat
   - sparse
   - sparse

+ 3 - 0
scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml

@@ -7,6 +7,7 @@
 packages:
 packages:
   - bash
   - bash
   - bc
   - bc
+  - bindgen
   - bison
   - bison
   - bsdextrautils
   - bsdextrautils
   - bzip2
   - bzip2
@@ -35,6 +36,7 @@ packages:
   - libcacard-dev
   - libcacard-dev
   - libcap-ng-dev
   - libcap-ng-dev
   - libcapstone-dev
   - libcapstone-dev
+  - libcbor-dev
   - libcmocka-dev
   - libcmocka-dev
   - libcurl4-gnutls-dev
   - libcurl4-gnutls-dev
   - libdaxctl-dev
   - libdaxctl-dev
@@ -111,6 +113,7 @@ packages:
   - python3-venv
   - python3-venv
   - python3-yaml
   - python3-yaml
   - rpm2cpio
   - rpm2cpio
+  - rustc
   - sed
   - sed
   - socat
   - socat
   - sparse
   - sparse

+ 0 - 0
scripts/meson-buildoptions.


+ 3 - 0
scripts/meson-buildoptions.sh

@@ -133,6 +133,7 @@ meson_options_help() {
   printf "%s\n" '  keyring         Linux keyring support'
   printf "%s\n" '  keyring         Linux keyring support'
   printf "%s\n" '  kvm             KVM acceleration support'
   printf "%s\n" '  kvm             KVM acceleration support'
   printf "%s\n" '  l2tpv3          l2tpv3 network backend support'
   printf "%s\n" '  l2tpv3          l2tpv3 network backend support'
+  printf "%s\n" '  libcbor         libcbor support'
   printf "%s\n" '  libdaxctl       libdaxctl support'
   printf "%s\n" '  libdaxctl       libdaxctl support'
   printf "%s\n" '  libdw           debuginfo support'
   printf "%s\n" '  libdw           debuginfo support'
   printf "%s\n" '  libiscsi        libiscsi userspace initiator'
   printf "%s\n" '  libiscsi        libiscsi userspace initiator'
@@ -358,6 +359,8 @@ _meson_option_parse() {
     --disable-kvm) printf "%s" -Dkvm=disabled ;;
     --disable-kvm) printf "%s" -Dkvm=disabled ;;
     --enable-l2tpv3) printf "%s" -Dl2tpv3=enabled ;;
     --enable-l2tpv3) printf "%s" -Dl2tpv3=enabled ;;
     --disable-l2tpv3) printf "%s" -Dl2tpv3=disabled ;;
     --disable-l2tpv3) printf "%s" -Dl2tpv3=disabled ;;
+    --enable-libcbor) printf "%s" -Dlibcbor=enabled ;;
+    --disable-libcbor) printf "%s" -Dlibcbor=disabled ;;
     --enable-libdaxctl) printf "%s" -Dlibdaxctl=enabled ;;
     --enable-libdaxctl) printf "%s" -Dlibdaxctl=enabled ;;
     --disable-libdaxctl) printf "%s" -Dlibdaxctl=disabled ;;
     --disable-libdaxctl) printf "%s" -Dlibdaxctl=disabled ;;
     --libdir=*) quote_sh "-Dlibdir=$2" ;;
     --libdir=*) quote_sh "-Dlibdir=$2" ;;

+ 6 - 1
stubs/meson.build

@@ -55,7 +55,12 @@ endif
 if have_user
 if have_user
   # Symbols that are used by hw/core.
   # Symbols that are used by hw/core.
   stub_ss.add(files('cpu-synchronize-state.c'))
   stub_ss.add(files('cpu-synchronize-state.c'))
-  stub_ss.add(files('qdev.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)
+    stub_ss.add(files('qdev.c'))
+  endif
 endif
 endif
 
 
 if have_system
 if have_system

+ 11 - 7
target/i386/cpu-dump.c

@@ -27,7 +27,7 @@
 /***********************************************************/
 /***********************************************************/
 /* x86 debug */
 /* x86 debug */
 
 
-static const char *cc_op_str[CC_OP_NB] = {
+static const char * const cc_op_str[] = {
     [CC_OP_DYNAMIC] = "DYNAMIC",
     [CC_OP_DYNAMIC] = "DYNAMIC",
 
 
     [CC_OP_EFLAGS] = "EFLAGS",
     [CC_OP_EFLAGS] = "EFLAGS",
@@ -91,7 +91,6 @@ static const char *cc_op_str[CC_OP_NB] = {
     [CC_OP_BMILGQ] = "BMILGQ",
     [CC_OP_BMILGQ] = "BMILGQ",
 
 
     [CC_OP_POPCNT] = "POPCNT",
     [CC_OP_POPCNT] = "POPCNT",
-    [CC_OP_CLR] = "CLR",
 };
 };
 
 
 static void
 static void
@@ -347,7 +346,6 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
     X86CPU *cpu = X86_CPU(cs);
     X86CPU *cpu = X86_CPU(cs);
     CPUX86State *env = &cpu->env;
     CPUX86State *env = &cpu->env;
     int eflags, i, nb;
     int eflags, i, nb;
-    char cc_op_name[32];
     static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };
     static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };
 
 
     eflags = cpu_compute_eflags(env);
     eflags = cpu_compute_eflags(env);
@@ -456,10 +454,16 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
                      env->dr[6], env->dr[7]);
                      env->dr[6], env->dr[7]);
     }
     }
     if (flags & CPU_DUMP_CCOP) {
     if (flags & CPU_DUMP_CCOP) {
-        if ((unsigned)env->cc_op < CC_OP_NB)
-            snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]);
-        else
-            snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op);
+        const char *cc_op_name = NULL;
+        char cc_op_buf[32];
+
+        if ((unsigned)env->cc_op < ARRAY_SIZE(cc_op_str)) {
+            cc_op_name = cc_op_str[env->cc_op];
+        }
+        if (cc_op_name == NULL) {
+            snprintf(cc_op_buf, sizeof(cc_op_buf), "[%d]", env->cc_op);
+            cc_op_name = cc_op_buf;
+        }
 #ifdef TARGET_X86_64
 #ifdef TARGET_X86_64
         if (env->hflags & HF_CS64_MASK) {
         if (env->hflags & HF_CS64_MASK) {
             qemu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%s\n",
             qemu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%s\n",

+ 195 - 23
target/i386/cpu.c

@@ -46,6 +46,9 @@
 #include "cpu-internal.h"
 #include "cpu-internal.h"
 
 
 static void x86_cpu_realizefn(DeviceState *dev, Error **errp);
 static void x86_cpu_realizefn(DeviceState *dev, Error **errp);
+static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index,
+                                        uint32_t *eax, uint32_t *ebx,
+                                        uint32_t *ecx, uint32_t *edx);
 
 
 /* Helpers for building CPUID[2] descriptors: */
 /* Helpers for building CPUID[2] descriptors: */
 
 
@@ -898,6 +901,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
 #define TCG_SGX_12_0_EAX_FEATURES 0
 #define TCG_SGX_12_0_EAX_FEATURES 0
 #define TCG_SGX_12_0_EBX_FEATURES 0
 #define TCG_SGX_12_0_EBX_FEATURES 0
 #define TCG_SGX_12_1_EAX_FEATURES 0
 #define TCG_SGX_12_1_EAX_FEATURES 0
+#define TCG_24_0_EBX_FEATURES 0
 
 
 #if defined CONFIG_USER_ONLY
 #if defined CONFIG_USER_ONLY
 #define CPUID_8000_0008_EBX_KERNEL_FEATURES (CPUID_8000_0008_EBX_IBPB | \
 #define CPUID_8000_0008_EBX_KERNEL_FEATURES (CPUID_8000_0008_EBX_IBPB | \
@@ -1132,7 +1136,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
             "avx-vnni-int8", "avx-ne-convert", NULL, NULL,
             "avx-vnni-int8", "avx-ne-convert", NULL, NULL,
             "amx-complex", NULL, "avx-vnni-int16", NULL,
             "amx-complex", NULL, "avx-vnni-int16", NULL,
             NULL, NULL, "prefetchiti", NULL,
             NULL, NULL, "prefetchiti", NULL,
-            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, "avx10",
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
@@ -1163,6 +1167,20 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
         },
         },
         .tcg_features = TCG_7_2_EDX_FEATURES,
         .tcg_features = TCG_7_2_EDX_FEATURES,
     },
     },
+    [FEAT_24_0_EBX] = {
+        .type = CPUID_FEATURE_WORD,
+        .feat_names = {
+            [16] = "avx10-128",
+            [17] = "avx10-256",
+            [18] = "avx10-512",
+        },
+        .cpuid = {
+            .eax = 0x24,
+            .needs_ecx = true, .ecx = 0,
+            .reg = R_EBX,
+        },
+        .tcg_features = TCG_24_0_EBX_FEATURES,
+    },
     [FEAT_8000_0007_EDX] = {
     [FEAT_8000_0007_EDX] = {
         .type = CPUID_FEATURE_WORD,
         .type = CPUID_FEATURE_WORD,
         .feat_names = {
         .feat_names = {
@@ -1220,13 +1238,35 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
             NULL, NULL, NULL, NULL,
-            NULL, NULL, NULL, "sbpb",
-            "ibpb-brtype", NULL, NULL, NULL,
+            "eraps", NULL, NULL, "sbpb",
+            "ibpb-brtype", "srso-no", "srso-user-kernel-no", NULL,
         },
         },
         .cpuid = { .eax = 0x80000021, .reg = R_EAX, },
         .cpuid = { .eax = 0x80000021, .reg = R_EAX, },
         .tcg_features = 0,
         .tcg_features = 0,
         .unmigratable_flags = 0,
         .unmigratable_flags = 0,
     },
     },
+    [FEAT_8000_0021_EBX] = {
+        .type = CPUID_FEATURE_WORD,
+        .cpuid = { .eax = 0x80000021, .reg = R_EBX, },
+        .tcg_features = 0,
+        .unmigratable_flags = 0,
+    },
+    [FEAT_8000_0022_EAX] = {
+        .type = CPUID_FEATURE_WORD,
+        .feat_names = {
+            "perfmon-v2", NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+            NULL, NULL, NULL, NULL,
+        },
+        .cpuid = { .eax = 0x80000022, .reg = R_EAX, },
+        .tcg_features = 0,
+        .unmigratable_flags = 0,
+    },
     [FEAT_XSAVE] = {
     [FEAT_XSAVE] = {
         .type = CPUID_FEATURE_WORD,
         .type = CPUID_FEATURE_WORD,
         .feat_names = {
         .feat_names = {
@@ -1296,7 +1336,9 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
             .needs_ecx = true, .ecx = 0,
             .needs_ecx = true, .ecx = 0,
             .reg = R_EAX,
             .reg = R_EAX,
         },
         },
-        .tcg_features = ~0U,
+        .tcg_features = XSTATE_FP_MASK | XSTATE_SSE_MASK |
+            XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK |
+            XSTATE_PKRU_MASK,
         .migratable_flags = XSTATE_FP_MASK | XSTATE_SSE_MASK |
         .migratable_flags = XSTATE_FP_MASK | XSTATE_SSE_MASK |
             XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK |
             XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK |
             XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK |
             XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK |
@@ -1309,7 +1351,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
             .needs_ecx = true, .ecx = 0,
             .needs_ecx = true, .ecx = 0,
             .reg = R_EDX,
             .reg = R_EDX,
         },
         },
-        .tcg_features = ~0U,
+        .tcg_features = 0U,
     },
     },
     /*Below are MSR exposed features*/
     /*Below are MSR exposed features*/
     [FEAT_ARCH_CAPABILITIES] = {
     [FEAT_ARCH_CAPABILITIES] = {
@@ -1745,6 +1787,22 @@ static FeatureDep feature_dependencies[] = {
         .from = { FEAT_7_0_EBX,             CPUID_7_0_EBX_SGX },
         .from = { FEAT_7_0_EBX,             CPUID_7_0_EBX_SGX },
         .to = { FEAT_SGX_12_1_EAX,          ~0ull },
         .to = { FEAT_SGX_12_1_EAX,          ~0ull },
     },
     },
+    {
+        .from = { FEAT_24_0_EBX,            CPUID_24_0_EBX_AVX10_128 },
+        .to = { FEAT_24_0_EBX,              CPUID_24_0_EBX_AVX10_256 },
+    },
+    {
+        .from = { FEAT_24_0_EBX,            CPUID_24_0_EBX_AVX10_256 },
+        .to = { FEAT_24_0_EBX,              CPUID_24_0_EBX_AVX10_512 },
+    },
+    {
+        .from = { FEAT_24_0_EBX,            CPUID_24_0_EBX_AVX10_VL_MASK },
+        .to = { FEAT_7_1_EDX,               CPUID_7_1_EDX_AVX10 },
+    },
+    {
+        .from = { FEAT_7_1_EDX,             CPUID_7_1_EDX_AVX10 },
+        .to = { FEAT_24_0_EBX,              ~0ull },
+    },
 };
 };
 
 
 typedef struct X86RegisterInfo32 {
 typedef struct X86RegisterInfo32 {
@@ -1965,6 +2023,7 @@ typedef struct X86CPUDefinition {
     int family;
     int family;
     int model;
     int model;
     int stepping;
     int stepping;
+    uint8_t avx10_version;
     FeatureWordArray features;
     FeatureWordArray features;
     const char *model_id;
     const char *model_id;
     const CPUCaches *const cache_info;
     const CPUCaches *const cache_info;
@@ -4344,6 +4403,23 @@ static const X86CPUDefinition builtin_x86_defs[] = {
         .model_id = "Intel Xeon Processor (GraniteRapids)",
         .model_id = "Intel Xeon Processor (GraniteRapids)",
         .versions = (X86CPUVersionDefinition[]) {
         .versions = (X86CPUVersionDefinition[]) {
             { .version = 1 },
             { .version = 1 },
+            {
+                .version = 2,
+                .props = (PropValue[]) {
+                    { "ss", "on" },
+                    { "tsc-adjust", "on" },
+                    { "cldemote", "on" },
+                    { "movdiri", "on" },
+                    { "movdir64b", "on" },
+                    { "avx10", "on" },
+                    { "avx10-128", "on" },
+                    { "avx10-256", "on" },
+                    { "avx10-512", "on" },
+                    { "avx10-version", "1" },
+                    { "stepping", "1" },
+                    { /* end of list */ }
+                }
+            },
             { /* end of list */ },
             { /* end of list */ },
         },
         },
     },
     },
@@ -5226,7 +5302,7 @@ static const X86CPUDefinition builtin_x86_defs[] = {
             CPUID_8000_0008_EBX_STIBP_ALWAYS_ON |
             CPUID_8000_0008_EBX_STIBP_ALWAYS_ON |
             CPUID_8000_0008_EBX_AMD_SSBD | CPUID_8000_0008_EBX_AMD_PSFD,
             CPUID_8000_0008_EBX_AMD_SSBD | CPUID_8000_0008_EBX_AMD_PSFD,
         .features[FEAT_8000_0021_EAX] =
         .features[FEAT_8000_0021_EAX] =
-            CPUID_8000_0021_EAX_No_NESTED_DATA_BP |
+            CPUID_8000_0021_EAX_NO_NESTED_DATA_BP |
             CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING |
             CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING |
             CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE |
             CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE |
             CPUID_8000_0021_EAX_AUTO_IBRS,
             CPUID_8000_0021_EAX_AUTO_IBRS,
@@ -5816,7 +5892,7 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
     }
     }
 }
 }
 
 
-static void x86_cpu_filter_features(X86CPU *cpu, bool verbose);
+static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose);
 
 
 /* Build a list with the name of all features on a feature word array */
 /* Build a list with the name of all features on a feature word array */
 static void x86_cpu_list_feature_names(FeatureWordArray features,
 static void x86_cpu_list_feature_names(FeatureWordArray features,
@@ -6307,6 +6383,9 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model)
      */
      */
     object_property_set_str(OBJECT(cpu), "vendor", def->vendor, &error_abort);
     object_property_set_str(OBJECT(cpu), "vendor", def->vendor, &error_abort);
 
 
+    object_property_set_uint(OBJECT(cpu), "avx10-version", def->avx10_version,
+                             &error_abort);
+
     x86_cpu_apply_version_props(cpu, model);
     x86_cpu_apply_version_props(cpu, model);
 
 
     /*
     /*
@@ -6835,6 +6914,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
         }
         }
         break;
         break;
     }
     }
+    case 0x24: {
+        *eax = 0;
+        *ebx = 0;
+        *ecx = 0;
+        *edx = 0;
+        if ((env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) && count == 0) {
+            *ebx = env->features[FEAT_24_0_EBX] | env->avx10_version;
+        }
+        break;
+    }
     case 0x40000000:
     case 0x40000000:
         /*
         /*
          * CPUID code in kvm_arch_init_vcpu() ignores stuff
          * CPUID code in kvm_arch_init_vcpu() ignores stuff
@@ -7010,6 +7099,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
             *edx = 0;
             *edx = 0;
         }
         }
         break;
         break;
+    case 0x80000022:
+        *eax = *ebx = *ecx = *edx = 0;
+        /* AMD Extended Performance Monitoring and Debug */
+        if (kvm_enabled() && cpu->enable_pmu &&
+            (env->features[FEAT_8000_0022_EAX] & CPUID_8000_0022_EAX_PERFMON_V2)) {
+            *eax |= CPUID_8000_0022_EAX_PERFMON_V2;
+            *ebx |= kvm_arch_get_supported_cpuid(cs->kvm_state, index, count,
+                                                 R_EBX) & 0xf;
+        }
+        break;
     case 0xC0000000:
     case 0xC0000000:
         *eax = env->cpuid_xlevel2;
         *eax = env->cpuid_xlevel2;
         *ebx = 0;
         *ebx = 0;
@@ -7043,8 +7142,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
         }
         }
         break;
         break;
     case 0x80000021:
     case 0x80000021:
+        *eax = *ebx = *ecx = *edx = 0;
         *eax = env->features[FEAT_8000_0021_EAX];
         *eax = env->features[FEAT_8000_0021_EAX];
-        *ebx = *ecx = *edx = 0;
+        *ebx = env->features[FEAT_8000_0021_EBX];
         break;
         break;
     default:
     default:
         /* reserved values: zero */
         /* reserved values: zero */
@@ -7067,6 +7167,23 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env)
 #endif
 #endif
 }
 }
 
 
+static bool cpuid_has_xsave_feature(CPUX86State *env, const ExtSaveArea *esa)
+{
+    if (!esa->size) {
+        return false;
+    }
+
+    if (env->features[esa->feature] & esa->bits) {
+        return true;
+    }
+    if (esa->feature == FEAT_7_0_EBX && esa->bits == CPUID_7_0_EBX_AVX512F
+        && (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10)) {
+        return true;
+    }
+
+    return false;
+}
+
 static void x86_cpu_reset_hold(Object *obj, ResetType type)
 static void x86_cpu_reset_hold(Object *obj, ResetType type)
 {
 {
     CPUState *cs = CPU(obj);
     CPUState *cs = CPU(obj);
@@ -7175,7 +7292,7 @@ static void x86_cpu_reset_hold(Object *obj, ResetType type)
         if (!((1 << i) & CPUID_XSTATE_XCR0_MASK)) {
         if (!((1 << i) & CPUID_XSTATE_XCR0_MASK)) {
             continue;
             continue;
         }
         }
-        if (env->features[esa->feature] & esa->bits) {
+        if (cpuid_has_xsave_feature(env, esa)) {
             xcr0 |= 1ull << i;
             xcr0 |= 1ull << i;
         }
         }
     }
     }
@@ -7313,7 +7430,7 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
     mask = 0;
     mask = 0;
     for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
     for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
         const ExtSaveArea *esa = &x86_ext_save_areas[i];
         const ExtSaveArea *esa = &x86_ext_save_areas[i];
-        if (env->features[esa->feature] & esa->bits) {
+        if (cpuid_has_xsave_feature(env, esa)) {
             mask |= (1ULL << i);
             mask |= (1ULL << i);
         }
         }
     }
     }
@@ -7406,6 +7523,12 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
                 ~env->user_features[w] &
                 ~env->user_features[w] &
                 ~feature_word_info[w].no_autoenable_flags;
                 ~feature_word_info[w].no_autoenable_flags;
         }
         }
+
+        if ((env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) && !env->avx10_version) {
+            uint32_t eax, ebx, ecx, edx;
+            x86_cpu_get_supported_cpuid(0x24, 0, &eax, &ebx, &ecx, &edx);
+            env->avx10_version = ebx & 0xff;
+        }
     }
     }
 
 
     for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) {
     for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) {
@@ -7469,6 +7592,11 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
             x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F);
             x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F);
         }
         }
 
 
+        /* Advanced Vector Extensions 10 (AVX10) requires CPUID[0x24] */
+        if (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) {
+            x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x24);
+        }
+
         /* SVM requires CPUID[0x8000000A] */
         /* SVM requires CPUID[0x8000000A] */
         if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
         if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
             x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A);
             x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A);
@@ -7512,13 +7640,17 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
  * Finishes initialization of CPUID data, filters CPU feature
  * Finishes initialization of CPUID data, filters CPU feature
  * words based on host availability of each feature.
  * words based on host availability of each feature.
  *
  *
- * Returns: 0 if all flags are supported by the host, non-zero otherwise.
+ * Returns: true if any flag is not supported by the host, false otherwise.
  */
  */
-static void x86_cpu_filter_features(X86CPU *cpu, bool verbose)
+static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose)
 {
 {
     CPUX86State *env = &cpu->env;
     CPUX86State *env = &cpu->env;
     FeatureWord w;
     FeatureWord w;
     const char *prefix = NULL;
     const char *prefix = NULL;
+    bool have_filtered_features;
+
+    uint32_t eax_0, ebx_0, ecx_0, edx_0;
+    uint32_t eax_1, ebx_1, ecx_1, edx_1;
 
 
     if (verbose) {
     if (verbose) {
         prefix = accel_uses_host_cpuid()
         prefix = accel_uses_host_cpuid()
@@ -7540,13 +7672,10 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose)
      */
      */
     if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) &&
     if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) &&
         kvm_enabled()) {
         kvm_enabled()) {
-        uint32_t eax_0, ebx_0, ecx_0, edx_0_unused;
-        uint32_t eax_1, ebx_1, ecx_1_unused, edx_1_unused;
-
         x86_cpu_get_supported_cpuid(0x14, 0,
         x86_cpu_get_supported_cpuid(0x14, 0,
-                                    &eax_0, &ebx_0, &ecx_0, &edx_0_unused);
+                                    &eax_0, &ebx_0, &ecx_0, &edx_0);
         x86_cpu_get_supported_cpuid(0x14, 1,
         x86_cpu_get_supported_cpuid(0x14, 1,
-                                    &eax_1, &ebx_1, &ecx_1_unused, &edx_1_unused);
+                                    &eax_1, &ebx_1, &ecx_1, &edx_1);
 
 
         if (!eax_0 ||
         if (!eax_0 ||
            ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) ||
            ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) ||
@@ -7566,6 +7695,28 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose)
             mark_unavailable_features(cpu, FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT, prefix);
             mark_unavailable_features(cpu, FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT, prefix);
         }
         }
     }
     }
+
+    have_filtered_features = x86_cpu_have_filtered_features(cpu);
+
+    if (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) {
+        x86_cpu_get_supported_cpuid(0x24, 0,
+                                    &eax_0, &ebx_0, &ecx_0, &edx_0);
+        uint8_t version = ebx_0 & 0xff;
+
+        if (version < env->avx10_version) {
+            if (prefix) {
+                warn_report("%s: avx10.%d. Adjust to avx10.%d",
+                            prefix, env->avx10_version, version);
+            }
+            env->avx10_version = version;
+            have_filtered_features = true;
+        }
+    } else if (env->avx10_version && prefix) {
+        warn_report("%s: avx10.%d.", prefix, env->avx10_version);
+        have_filtered_features = true;
+    }
+
+    return have_filtered_features;
 }
 }
 
 
 static void x86_cpu_hyperv_realize(X86CPU *cpu)
 static void x86_cpu_hyperv_realize(X86CPU *cpu)
@@ -7663,14 +7814,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
         }
         }
     }
     }
 
 
-    x86_cpu_filter_features(cpu, cpu->check_cpuid || cpu->enforce_cpuid);
-
-    if (cpu->enforce_cpuid && x86_cpu_have_filtered_features(cpu)) {
-        error_setg(&local_err,
-                   accel_uses_host_cpuid() ?
+    if (x86_cpu_filter_features(cpu, cpu->check_cpuid || cpu->enforce_cpuid)) {
+        if (cpu->enforce_cpuid) {
+            error_setg(&local_err,
+                       accel_uses_host_cpuid() ?
                        "Host doesn't support requested features" :
                        "Host doesn't support requested features" :
                        "TCG doesn't support requested features");
                        "TCG doesn't support requested features");
-        goto out;
+            goto out;
+        }
     }
     }
 
 
     /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
     /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
@@ -7985,6 +8136,26 @@ static void x86_cpu_register_feature_bit_props(X86CPUClass *xcc,
 
 
 static void x86_cpu_post_initfn(Object *obj)
 static void x86_cpu_post_initfn(Object *obj)
 {
 {
+    static bool first = true;
+    uint64_t supported_xcr0;
+    int i;
+
+    if (first) {
+        first = false;
+
+        supported_xcr0 =
+            ((uint64_t) x86_cpu_get_supported_feature_word(NULL, FEAT_XSAVE_XCR0_HI) << 32) |
+            x86_cpu_get_supported_feature_word(NULL, FEAT_XSAVE_XCR0_LO);
+
+        for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
+            ExtSaveArea *esa = &x86_ext_save_areas[i];
+
+            if (!(supported_xcr0 & (1 << i))) {
+                esa->size = 0;
+            }
+        }
+    }
+
     accel_cpu_instance_init(CPU(obj));
     accel_cpu_instance_init(CPU(obj));
 }
 }
 
 
@@ -8329,6 +8500,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_UINT32("min-level", X86CPU, env.cpuid_min_level, 0),
     DEFINE_PROP_UINT32("min-level", X86CPU, env.cpuid_min_level, 0),
     DEFINE_PROP_UINT32("min-xlevel", X86CPU, env.cpuid_min_xlevel, 0),
     DEFINE_PROP_UINT32("min-xlevel", X86CPU, env.cpuid_min_xlevel, 0),
     DEFINE_PROP_UINT32("min-xlevel2", X86CPU, env.cpuid_min_xlevel2, 0),
     DEFINE_PROP_UINT32("min-xlevel2", X86CPU, env.cpuid_min_xlevel2, 0),
+    DEFINE_PROP_UINT8("avx10-version", X86CPU, env.avx10_version, 0),
     DEFINE_PROP_UINT64("ucode-rev", X86CPU, ucode_rev, 0),
     DEFINE_PROP_UINT64("ucode-rev", X86CPU, ucode_rev, 0),
     DEFINE_PROP_BOOL("full-cpuid-auto-level", X86CPU, full_cpuid_auto_level, true),
     DEFINE_PROP_BOOL("full-cpuid-auto-level", X86CPU, full_cpuid_auto_level, true),
     DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor),
     DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor),

+ 65 - 13
target/i386/cpu.h

@@ -24,6 +24,7 @@
 #include "cpu-qom.h"
 #include "cpu-qom.h"
 #include "kvm/hyperv-proto.h"
 #include "kvm/hyperv-proto.h"
 #include "exec/cpu-defs.h"
 #include "exec/cpu-defs.h"
+#include "exec/memop.h"
 #include "hw/i386/topology.h"
 #include "hw/i386/topology.h"
 #include "qapi/qapi-types-common.h"
 #include "qapi/qapi-types-common.h"
 #include "qemu/cpu-float.h"
 #include "qemu/cpu-float.h"
@@ -634,6 +635,8 @@ typedef enum FeatureWord {
     FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
     FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
     FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
     FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
     FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */
     FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */
+    FEAT_8000_0021_EBX, /* CPUID[8000_0021].EBX */
+    FEAT_8000_0022_EAX, /* CPUID[8000_0022].EAX */
     FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
     FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
     FEAT_KVM,           /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
     FEAT_KVM,           /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
     FEAT_KVM_HINTS,     /* CPUID[4000_0001].EDX */
     FEAT_KVM_HINTS,     /* CPUID[4000_0001].EDX */
@@ -662,6 +665,7 @@ typedef enum FeatureWord {
     FEAT_XSAVE_XSS_HI,     /* CPUID[EAX=0xd,ECX=1].EDX */
     FEAT_XSAVE_XSS_HI,     /* CPUID[EAX=0xd,ECX=1].EDX */
     FEAT_7_1_EDX,       /* CPUID[EAX=7,ECX=1].EDX */
     FEAT_7_1_EDX,       /* CPUID[EAX=7,ECX=1].EDX */
     FEAT_7_2_EDX,       /* CPUID[EAX=7,ECX=2].EDX */
     FEAT_7_2_EDX,       /* CPUID[EAX=7,ECX=2].EDX */
+    FEAT_24_0_EBX,      /* CPUID[EAX=0x24,ECX=0].EBX */
     FEATURE_WORDS,
     FEATURE_WORDS,
 } FeatureWord;
 } FeatureWord;
 
 
@@ -972,6 +976,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
 #define CPUID_7_1_EDX_AMX_COMPLEX       (1U << 8)
 #define CPUID_7_1_EDX_AMX_COMPLEX       (1U << 8)
 /* PREFETCHIT0/1 Instructions */
 /* PREFETCHIT0/1 Instructions */
 #define CPUID_7_1_EDX_PREFETCHITI       (1U << 14)
 #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) */
 /* Flexible return and event delivery (FRED) */
 #define CPUID_7_1_EAX_FRED              (1U << 17)
 #define CPUID_7_1_EAX_FRED              (1U << 17)
 /* Load into IA32_KERNEL_GS_BASE (LKGS) */
 /* Load into IA32_KERNEL_GS_BASE (LKGS) */
@@ -988,6 +994,17 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
 /* Packets which contain IP payload have LIP values */
 /* Packets which contain IP payload have LIP values */
 #define CPUID_14_0_ECX_LIP              (1U << 31)
 #define CPUID_14_0_ECX_LIP              (1U << 31)
 
 
+/* AVX10 128-bit vector support is present */
+#define CPUID_24_0_EBX_AVX10_128        (1U << 16)
+/* AVX10 256-bit vector support is present */
+#define CPUID_24_0_EBX_AVX10_256        (1U << 17)
+/* AVX10 512-bit vector support is present */
+#define CPUID_24_0_EBX_AVX10_512        (1U << 18)
+/* AVX10 vector length support mask */
+#define CPUID_24_0_EBX_AVX10_VL_MASK    (CPUID_24_0_EBX_AVX10_128 | \
+                                         CPUID_24_0_EBX_AVX10_256 | \
+                                         CPUID_24_0_EBX_AVX10_512)
+
 /* RAS Features */
 /* RAS Features */
 #define CPUID_8000_0007_EBX_OVERFLOW_RECOV    (1U << 0)
 #define CPUID_8000_0007_EBX_OVERFLOW_RECOV    (1U << 0)
 #define CPUID_8000_0007_EBX_SUCCOR      (1U << 1)
 #define CPUID_8000_0007_EBX_SUCCOR      (1U << 1)
@@ -1014,13 +1031,32 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
 #define CPUID_8000_0008_EBX_AMD_PSFD    (1U << 28)
 #define CPUID_8000_0008_EBX_AMD_PSFD    (1U << 28)
 
 
 /* Processor ignores nested data breakpoints */
 /* Processor ignores nested data breakpoints */
-#define CPUID_8000_0021_EAX_No_NESTED_DATA_BP    (1U << 0)
+#define CPUID_8000_0021_EAX_NO_NESTED_DATA_BP            (1U << 0)
 /* LFENCE is always serializing */
 /* LFENCE is always serializing */
 #define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING    (1U << 2)
 #define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING    (1U << 2)
 /* Null Selector Clears Base */
 /* Null Selector Clears Base */
-#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE    (1U << 6)
+#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE            (1U << 6)
 /* Automatic IBRS */
 /* Automatic IBRS */
-#define CPUID_8000_0021_EAX_AUTO_IBRS   (1U << 8)
+#define CPUID_8000_0021_EAX_AUTO_IBRS                    (1U << 8)
+/* Enhanced Return Address Predictor Scurity */
+#define CPUID_8000_0021_EAX_ERAPS                        (1U << 24)
+/* Selective Branch Predictor Barrier */
+#define CPUID_8000_0021_EAX_SBPB                         (1U << 27)
+/* IBPB includes branch type prediction flushing */
+#define CPUID_8000_0021_EAX_IBPB_BRTYPE                  (1U << 28)
+/* Not vulnerable to Speculative Return Stack Overflow */
+#define CPUID_8000_0021_EAX_SRSO_NO                      (1U << 29)
+/* Not vulnerable to SRSO at the user-kernel boundary */
+#define CPUID_8000_0021_EAX_SRSO_USER_KERNEL_NO          (1U << 30)
+
+/*
+ * Return Address Predictor size. RapSize x 8 is the minimum number of
+ * CALL instructions software needs to execute to flush the RAP.
+ */
+#define CPUID_8000_0021_EBX_RAPSIZE    (8U << 16)
+
+/* Performance Monitoring Version 2 */
+#define CPUID_8000_0022_EAX_PERFMON_V2  (1U << 0)
 
 
 #define CPUID_XSAVE_XSAVEOPT   (1U << 0)
 #define CPUID_XSAVE_XSAVEOPT   (1U << 0)
 #define CPUID_XSAVE_XSAVEC     (1U << 1)
 #define CPUID_XSAVE_XSAVEC     (1U << 1)
@@ -1278,14 +1314,14 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
  * are only needed for conditional branches.
  * are only needed for conditional branches.
  */
  */
 typedef enum {
 typedef enum {
-    CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
-    CC_OP_EFLAGS,  /* all cc are explicitly computed, CC_SRC = flags */
-    CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest.  */
-    CC_OP_ADOX, /* CC_SRC2 = O, CC_SRC = rest.  */
-    CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest.  */
-    CC_OP_CLR, /* Z and P set, all other flags clear.  */
-
-    CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */
+    CC_OP_EFLAGS = 0,  /* all cc are explicitly computed, CC_SRC = flags */
+    CC_OP_ADCX = 1,    /* CC_DST = C, CC_SRC = rest.  */
+    CC_OP_ADOX = 2,    /* CC_SRC2 = O, CC_SRC = rest.  */
+    CC_OP_ADCOX = 3,   /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest.  */
+
+    /* Low 2 bits = MemOp constant for the size */
+#define CC_OP_FIRST_BWLQ CC_OP_MULB
+    CC_OP_MULB = 4, /* modify all flags, C, O = (CC_SRC != 0) */
     CC_OP_MULW,
     CC_OP_MULW,
     CC_OP_MULL,
     CC_OP_MULL,
     CC_OP_MULQ,
     CC_OP_MULQ,
@@ -1355,10 +1391,24 @@ typedef enum {
     CC_OP_POPCNTL__,
     CC_OP_POPCNTL__,
     CC_OP_POPCNTQ__,
     CC_OP_POPCNTQ__,
     CC_OP_POPCNT = sizeof(target_ulong) == 8 ? CC_OP_POPCNTQ__ : CC_OP_POPCNTL__,
     CC_OP_POPCNT = sizeof(target_ulong) == 8 ? CC_OP_POPCNTQ__ : CC_OP_POPCNTL__,
+#define CC_OP_LAST_BWLQ CC_OP_POPCNTQ__
 
 
-    CC_OP_NB,
+    CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
 } CCOp;
 } CCOp;
-QEMU_BUILD_BUG_ON(CC_OP_NB >= 128);
+
+/* See X86DecodedInsn.cc_op, using int8_t. */
+QEMU_BUILD_BUG_ON(CC_OP_DYNAMIC > INT8_MAX);
+
+static inline MemOp cc_op_size(CCOp op)
+{
+    MemOp size = op & 3;
+
+    QEMU_BUILD_BUG_ON(CC_OP_FIRST_BWLQ & 3);
+    assert(op >= CC_OP_FIRST_BWLQ && op <= CC_OP_LAST_BWLQ);
+    assert(size <= MO_TL);
+
+    return size;
+}
 
 
 typedef struct SegmentCache {
 typedef struct SegmentCache {
     uint32_t selector;
     uint32_t selector;
@@ -1918,6 +1968,8 @@ typedef struct CPUArchState {
     uint32_t cpuid_vendor3;
     uint32_t cpuid_vendor3;
     uint32_t cpuid_version;
     uint32_t cpuid_version;
     FeatureWordArray features;
     FeatureWordArray features;
+    /* AVX10 version */
+    uint8_t avx10_version;
     /* Features that were explicitly enabled/disabled */
     /* Features that were explicitly enabled/disabled */
     FeatureWordArray user_features;
     FeatureWordArray user_features;
     uint32_t cpuid_model[12];
     uint32_t cpuid_model[12];

+ 1 - 0
target/i386/helper.h

@@ -1,5 +1,6 @@
 DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
 DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
 DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
 DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int)
+DEF_HELPER_FLAGS_3(cc_compute_nz, TCG_CALL_NO_RWG_SE, tl, tl, tl, int)
 
 
 DEF_HELPER_3(write_eflags, void, env, tl, i32)
 DEF_HELPER_3(write_eflags, void, env, tl, i32)
 DEF_HELPER_1(read_eflags, tl, env)
 DEF_HELPER_1(read_eflags, tl, env)

+ 3 - 13
target/i386/host-cpu.c

@@ -42,7 +42,7 @@ static uint32_t host_cpu_phys_bits(void)
     return host_phys_bits;
     return host_phys_bits;
 }
 }
 
 
-static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
+static void host_cpu_adjust_phys_bits(X86CPU *cpu)
 {
 {
     uint32_t host_phys_bits = host_cpu_phys_bits();
     uint32_t host_phys_bits = host_cpu_phys_bits();
     uint32_t phys_bits = cpu->phys_bits;
     uint32_t phys_bits = cpu->phys_bits;
@@ -66,7 +66,7 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu)
         }
         }
     }
     }
 
 
-    return phys_bits;
+    cpu->phys_bits = phys_bits;
 }
 }
 
 
 bool host_cpu_realizefn(CPUState *cs, Error **errp)
 bool host_cpu_realizefn(CPUState *cs, Error **errp)
@@ -75,17 +75,7 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp)
     CPUX86State *env = &cpu->env;
     CPUX86State *env = &cpu->env;
 
 
     if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
     if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
-        uint32_t phys_bits = host_cpu_adjust_phys_bits(cpu);
-
-        if (phys_bits &&
-            (phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
-             phys_bits < 32)) {
-            error_setg(errp, "phys-bits should be between 32 and %u "
-                       " (but is %u)",
-                       TARGET_PHYS_ADDR_SPACE_BITS, phys_bits);
-            return false;
-        }
-        cpu->phys_bits = phys_bits;
+        host_cpu_adjust_phys_bits(cpu);
     }
     }
     return true;
     return true;
 }
 }

+ 31 - 21
target/i386/hvf/x86_cpuid.c

@@ -21,28 +21,38 @@
  */
  */
 
 
 #include "qemu/osdep.h"
 #include "qemu/osdep.h"
+#include "qemu/cpuid.h"
+#include "host/cpuinfo.h"
 #include "cpu.h"
 #include "cpu.h"
 #include "x86.h"
 #include "x86.h"
 #include "vmx.h"
 #include "vmx.h"
 #include "sysemu/hvf.h"
 #include "sysemu/hvf.h"
 #include "hvf-i386.h"
 #include "hvf-i386.h"
 
 
-static bool xgetbv(uint32_t cpuid_ecx, uint32_t idx, uint64_t *xcr)
+static bool cached_xcr0;
+static uint64_t supported_xcr0;
+
+static void cache_host_xcr0()
 {
 {
-    uint32_t xcrl, xcrh;
+    if (cached_xcr0) {
+        return;
+    }
 
 
-    if (cpuid_ecx & CPUID_EXT_OSXSAVE) {
-        /*
-         * The xgetbv instruction is not available to older versions of
-         * the assembler, so we encode the instruction manually.
-         */
-        asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcrl), "=d" (xcrh) : "c" (idx));
+    if (cpuinfo & CPUINFO_OSXSAVE) {
+        uint64_t host_xcr0 = xgetbv_low(0);
 
 
-        *xcr = (((uint64_t)xcrh) << 32) | xcrl;
-        return true;
+        /* Only show xcr0 bits corresponding to usable features.  */
+        supported_xcr0 = host_xcr0 & (XSTATE_FP_MASK |
+                                      XSTATE_SSE_MASK | XSTATE_YMM_MASK |
+                                      XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK |
+                                      XSTATE_Hi16_ZMM_MASK);
+        if ((supported_xcr0 & (XSTATE_FP_MASK | XSTATE_SSE_MASK)) !=
+            (XSTATE_FP_MASK | XSTATE_SSE_MASK)) {
+            supported_xcr0 = 0;
+        }
     }
     }
 
 
-    return false;
+    cached_xcr0 = true;
 }
 }
 
 
 uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
 uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
@@ -51,6 +61,7 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
     uint64_t cap;
     uint64_t cap;
     uint32_t eax, ebx, ecx, edx;
     uint32_t eax, ebx, ecx, edx;
 
 
+    cache_host_xcr0();
     host_cpuid(func, idx, &eax, &ebx, &ecx, &edx);
     host_cpuid(func, idx, &eax, &ebx, &ecx, &edx);
 
 
     switch (func) {
     switch (func) {
@@ -66,7 +77,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
         ecx &= CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 |
         ecx &= CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 |
              CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID |
              CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID |
              CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_MOVBE |
              CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_MOVBE |
-             CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_XSAVE |
+             CPUID_EXT_POPCNT | CPUID_EXT_AES |
+             (supported_xcr0 ? CPUID_EXT_XSAVE : 0) |
              CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND;
              CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND;
         ecx |= CPUID_EXT_HYPERVISOR;
         ecx |= CPUID_EXT_HYPERVISOR;
         break;
         break;
@@ -107,16 +119,14 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
         eax = 0;
         eax = 0;
         break;
         break;
     case 0xD:
     case 0xD:
+        if (!supported_xcr0 ||
+            (idx > 1 && !(supported_xcr0 & (1 << idx)))) {
+            eax = ebx = ecx = edx = 0;
+            break;
+        }
+
         if (idx == 0) {
         if (idx == 0) {
-            uint64_t host_xcr0;
-            if (xgetbv(ecx, 0, &host_xcr0)) {
-                uint64_t supp_xcr0 = host_xcr0 & (XSTATE_FP_MASK |
-                                  XSTATE_SSE_MASK | XSTATE_YMM_MASK |
-                                  XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK |
-                                  XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK |
-                                  XSTATE_Hi16_ZMM_MASK);
-                eax &= supp_xcr0;
-            }
+            eax = supported_xcr0;
         } else if (idx == 1) {
         } else if (idx == 1) {
             hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap);
             hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap);
             eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1;
             eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1;

+ 0 - 4
target/i386/kvm/kvm-cpu.c

@@ -143,10 +143,6 @@ static void kvm_cpu_xsave_init(void)
         if (!esa->size) {
         if (!esa->size) {
             continue;
             continue;
         }
         }
-        if ((x86_cpu_get_supported_feature_word(NULL, esa->feature) & esa->bits)
-            != esa->bits) {
-            continue;
-        }
         host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx);
         host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx);
         if (eax != 0) {
         if (eax != 0) {
             assert(esa->size == eax);
             assert(esa->size == eax);

+ 2 - 1
target/i386/kvm/kvm.c

@@ -1923,7 +1923,8 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env,
         case 0x7:
         case 0x7:
         case 0x14:
         case 0x14:
         case 0x1d:
         case 0x1d:
-        case 0x1e: {
+        case 0x1e:
+        case 0x24: {
             uint32_t times;
             uint32_t times;
 
 
             c->function = i;
             c->function = i;

+ 13 - 38
target/i386/tcg/cc_helper.c

@@ -22,41 +22,6 @@
 #include "exec/helper-proto.h"
 #include "exec/helper-proto.h"
 #include "helper-tcg.h"
 #include "helper-tcg.h"
 
 
-const uint8_t parity_table[256] = {
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-};
-
 #define SHIFT 0
 #define SHIFT 0
 #include "cc_helper_template.h.inc"
 #include "cc_helper_template.h.inc"
 #undef SHIFT
 #undef SHIFT
@@ -95,6 +60,19 @@ static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1,
     return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O);
     return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O);
 }
 }
 
 
+target_ulong helper_cc_compute_nz(target_ulong dst, target_ulong src1,
+                                  int op)
+{
+    if (CC_OP_HAS_EFLAGS(op)) {
+        return ~src1 & CC_Z;
+    } else {
+        MemOp size = cc_op_size(op);
+        target_ulong mask = MAKE_64BIT_MASK(0, 8 << size);
+
+        return dst & mask;
+    }
+}
+
 target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1,
 target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1,
                                    target_ulong src2, int op)
                                    target_ulong src2, int op)
 {
 {
@@ -104,8 +82,6 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1,
 
 
     case CC_OP_EFLAGS:
     case CC_OP_EFLAGS:
         return src1;
         return src1;
-    case CC_OP_CLR:
-        return CC_Z | CC_P;
     case CC_OP_POPCNT:
     case CC_OP_POPCNT:
         return dst ? 0 : CC_Z;
         return dst ? 0 : CC_Z;
 
 
@@ -243,7 +219,6 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1,
     case CC_OP_LOGICW:
     case CC_OP_LOGICW:
     case CC_OP_LOGICL:
     case CC_OP_LOGICL:
     case CC_OP_LOGICQ:
     case CC_OP_LOGICQ:
-    case CC_OP_CLR:
     case CC_OP_POPCNT:
     case CC_OP_POPCNT:
         return 0;
         return 0;
 
 

+ 82 - 45
target/i386/tcg/cc_helper_template.h.inc

@@ -22,12 +22,17 @@
 #if DATA_BITS == 8
 #if DATA_BITS == 8
 #define SUFFIX b
 #define SUFFIX b
 #define DATA_TYPE uint8_t
 #define DATA_TYPE uint8_t
+#define WIDER_TYPE uint32_t
 #elif DATA_BITS == 16
 #elif DATA_BITS == 16
 #define SUFFIX w
 #define SUFFIX w
 #define DATA_TYPE uint16_t
 #define DATA_TYPE uint16_t
+#define WIDER_TYPE uint32_t
 #elif DATA_BITS == 32
 #elif DATA_BITS == 32
 #define SUFFIX l
 #define SUFFIX l
 #define DATA_TYPE uint32_t
 #define DATA_TYPE uint32_t
+#if HOST_LONG_BITS >= 64
+#define WIDER_TYPE uint64_t
+#endif
 #elif DATA_BITS == 64
 #elif DATA_BITS == 64
 #define SUFFIX q
 #define SUFFIX q
 #define DATA_TYPE uint64_t
 #define DATA_TYPE uint64_t
@@ -39,18 +44,18 @@
 
 
 /* dynamic flags computation */
 /* dynamic flags computation */
 
 
-static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
     DATA_TYPE src2 = dst - src1;
     DATA_TYPE src2 = dst - src1;
 
 
     cf = dst < src1;
     cf = dst < src1;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & CC_A;
     af = (dst ^ src1 ^ src2) & CC_A;
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
     of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
@@ -58,39 +63,54 @@ static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
     return dst < src1;
     return dst < src1;
 }
 }
 
 
-static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1,
+static uint32_t glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1,
                                          DATA_TYPE src3)
                                          DATA_TYPE src3)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
+
+#ifdef WIDER_TYPE
+    WIDER_TYPE src13 = (WIDER_TYPE) src1 + (WIDER_TYPE) src3;
+    DATA_TYPE src2 = dst - src13;
+
+    cf = dst < src13;
+#else
     DATA_TYPE src2 = dst - src1 - src3;
     DATA_TYPE src2 = dst - src1 - src3;
 
 
     cf = (src3 ? dst <= src1 : dst < src1);
     cf = (src3 ? dst <= src1 : dst < src1);
-    pf = parity_table[(uint8_t)dst];
+#endif
+
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & 0x10;
     af = (dst ^ src1 ^ src2) & 0x10;
     zf = (dst == 0) << 6;
     zf = (dst == 0) << 6;
     sf = lshift(dst, 8 - DATA_BITS) & 0x80;
     sf = lshift(dst, 8 - DATA_BITS) & 0x80;
     of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
     of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1,
 static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1,
                                        DATA_TYPE src3)
                                        DATA_TYPE src3)
 {
 {
+#ifdef WIDER_TYPE
+    WIDER_TYPE src13 = (WIDER_TYPE) src1 + (WIDER_TYPE) src3;
+
+    return dst < src13;
+#else
     return src3 ? dst <= src1 : dst < src1;
     return src3 ? dst <= src1 : dst < src1;
+#endif
 }
 }
 
 
-static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2)
+static uint32_t glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
     DATA_TYPE src1 = dst + src2;
     DATA_TYPE src1 = dst + src2;
 
 
     cf = src1 < src2;
     cf = src1 < src2;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & CC_A;
     af = (dst ^ src1 ^ src2) & CC_A;
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
     of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2)
 static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2)
@@ -100,86 +120,102 @@ static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2)
     return src1 < src2;
     return src1 < src2;
 }
 }
 
 
-static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2,
+static uint32_t glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2,
                                          DATA_TYPE src3)
                                          DATA_TYPE src3)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
+
+#ifdef WIDER_TYPE
+    WIDER_TYPE src23 = (WIDER_TYPE) src2 + (WIDER_TYPE) src3;
+    DATA_TYPE src1 = dst + src23;
+
+    cf = src1 < src23;
+#else
     DATA_TYPE src1 = dst + src2 + src3;
     DATA_TYPE src1 = dst + src2 + src3;
 
 
     cf = (src3 ? src1 <= src2 : src1 < src2);
     cf = (src3 ? src1 <= src2 : src1 < src2);
-    pf = parity_table[(uint8_t)dst];
+#endif
+
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & 0x10;
     af = (dst ^ src1 ^ src2) & 0x10;
     zf = (dst == 0) << 6;
     zf = (dst == 0) << 6;
     sf = lshift(dst, 8 - DATA_BITS) & 0x80;
     sf = lshift(dst, 8 - DATA_BITS) & 0x80;
     of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
     of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2,
 static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2,
                                        DATA_TYPE src3)
                                        DATA_TYPE src3)
 {
 {
+#ifdef WIDER_TYPE
+    WIDER_TYPE src23 = (WIDER_TYPE) src2 + (WIDER_TYPE) src3;
+    DATA_TYPE src1 = dst + src23;
+
+    return src1 < src23;
+#else
     DATA_TYPE src1 = dst + src2 + src3;
     DATA_TYPE src1 = dst + src2 + src3;
 
 
     return (src3 ? src1 <= src2 : src1 < src2);
     return (src3 ? src1 <= src2 : src1 < src2);
+#endif
 }
 }
 
 
-static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = 0;
     cf = 0;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = 0;
     af = 0;
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = 0;
     of = 0;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
-static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
     DATA_TYPE src2;
     DATA_TYPE src2;
 
 
     cf = src1;
     cf = src1;
     src1 = dst - 1;
     src1 = dst - 1;
     src2 = 1;
     src2 = 1;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & CC_A;
     af = (dst ^ src1 ^ src2) & CC_A;
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = (dst == SIGN_MASK) * CC_O;
     of = (dst == SIGN_MASK) * CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
-static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
     DATA_TYPE src2;
     DATA_TYPE src2;
 
 
     cf = src1;
     cf = src1;
     src1 = dst + 1;
     src1 = dst + 1;
     src2 = 1;
     src2 = 1;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = (dst ^ src1 ^ src2) & CC_A;
     af = (dst ^ src1 ^ src2) & CC_A;
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = (dst == SIGN_MASK - 1) * CC_O;
     of = (dst == SIGN_MASK - 1) * CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
-static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = (src1 >> (DATA_BITS - 1)) & CC_C;
     cf = (src1 >> (DATA_BITS - 1)) & CC_C;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = 0; /* undefined */
     af = 0; /* undefined */
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     /* of is defined iff shift count == 1 */
     /* of is defined iff shift count == 1 */
     of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O;
     of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
@@ -187,39 +223,39 @@ static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
     return (src1 >> (DATA_BITS - 1)) & CC_C;
     return (src1 >> (DATA_BITS - 1)) & CC_C;
 }
 }
 
 
-static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = src1 & 1;
     cf = src1 & 1;
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = 0; /* undefined */
     af = 0; /* undefined */
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     /* of is defined iff shift count == 1 */
     /* of is defined iff shift count == 1 */
     of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O;
     of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 /* NOTE: we compute the flags like the P4. On olders CPUs, only OF and
 /* NOTE: we compute the flags like the P4. On olders CPUs, only OF and
    CF are modified and it is slower to do that.  Note as well that we
    CF are modified and it is slower to do that.  Note as well that we
    don't truncate SRC1 for computing carry to DATA_TYPE.  */
    don't truncate SRC1 for computing carry to DATA_TYPE.  */
-static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1)
+static uint32_t glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = (src1 != 0);
     cf = (src1 != 0);
-    pf = parity_table[(uint8_t)dst];
+    pf = compute_pf(dst);
     af = 0; /* undefined */
     af = 0; /* undefined */
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = cf * CC_O;
     of = cf * CC_O;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
-static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
+static uint32_t glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = (src1 == 0);
     cf = (src1 == 0);
     pf = 0; /* undefined */
     pf = 0; /* undefined */
@@ -227,7 +263,7 @@ static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = 0;
     of = 0;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
@@ -237,7 +273,7 @@ static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 
 
 static int glue(compute_all_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 static int glue(compute_all_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 {
 {
-    int cf, pf, af, zf, sf, of;
+    uint32_t cf, pf, af, zf, sf, of;
 
 
     cf = (src1 != 0);
     cf = (src1 != 0);
     pf = 0; /* undefined */
     pf = 0; /* undefined */
@@ -245,7 +281,7 @@ static int glue(compute_all_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
     zf = (dst == 0) * CC_Z;
     zf = (dst == 0) * CC_Z;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     sf = lshift(dst, 8 - DATA_BITS) & CC_S;
     of = 0;
     of = 0;
-    return cf | pf | af | zf | sf | of;
+    return cf + pf + af + zf + sf + of;
 }
 }
 
 
 static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
@@ -258,3 +294,4 @@ static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1)
 #undef DATA_TYPE
 #undef DATA_TYPE
 #undef DATA_MASK
 #undef DATA_MASK
 #undef SUFFIX
 #undef SUFFIX
+#undef WIDER_TYPE

+ 3 - 3
target/i386/tcg/decode-new.c.inc

@@ -345,9 +345,9 @@ static void decode_group15(DisasContext *s, CPUX86State *env, X86OpEntry *entry,
         [1] = X86_OP_ENTRYw(RDxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3),
         [1] = X86_OP_ENTRYw(RDxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3),
         [2] = X86_OP_ENTRYr(WRxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0),
         [2] = X86_OP_ENTRYr(WRxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0),
         [3] = X86_OP_ENTRYr(WRxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0),
         [3] = X86_OP_ENTRYr(WRxxBASE,   R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0),
-        [5] = X86_OP_ENTRY0(LFENCE,          cpuid(SSE2) p_00),
+        [5] = X86_OP_ENTRY0(LFENCE,          cpuid(SSE) p_00),
         [6] = X86_OP_ENTRY0(MFENCE,          cpuid(SSE2) p_00),
         [6] = X86_OP_ENTRY0(MFENCE,          cpuid(SSE2) p_00),
-        [7] = X86_OP_ENTRY0(SFENCE,          cpuid(SSE2) p_00),
+        [7] = X86_OP_ENTRY0(SFENCE,          cpuid(SSE) p_00),
     };
     };
 
 
     static const X86OpEntry group15_mem[8] = {
     static const X86OpEntry group15_mem[8] = {
@@ -2865,7 +2865,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu)
             tcg_gen_mov_i32(cpu_cc_op, decode.cc_op_dynamic);
             tcg_gen_mov_i32(cpu_cc_op, decode.cc_op_dynamic);
         }
         }
         set_cc_op(s, decode.cc_op);
         set_cc_op(s, decode.cc_op);
-        cc_live = cc_op_live[decode.cc_op];
+        cc_live = cc_op_live(decode.cc_op);
     } else {
     } else {
         cc_live = 0;
         cc_live = 0;
     }
     }

+ 9 - 15
target/i386/tcg/emit.c.inc

@@ -1452,19 +1452,12 @@ static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv
      * C is the result of the test, Z is unchanged, and the others
      * C is the result of the test, Z is unchanged, and the others
      * are all undefined.
      * are all undefined.
      */
      */
-    switch (s->cc_op) {
-    case CC_OP_DYNAMIC:
-    case CC_OP_CLR:
-    case CC_OP_EFLAGS:
-    case CC_OP_ADCX:
-    case CC_OP_ADOX:
-    case CC_OP_ADCOX:
+    if (s->cc_op == CC_OP_DYNAMIC || CC_OP_HAS_EFLAGS(s->cc_op)) {
         /* Generate EFLAGS and replace the C bit.  */
         /* Generate EFLAGS and replace the C bit.  */
         cf = tcg_temp_new();
         cf = tcg_temp_new();
         tcg_gen_setcond_tl(TCG_COND_TSTNE, cf, src, mask);
         tcg_gen_setcond_tl(TCG_COND_TSTNE, cf, src, mask);
         prepare_update_cf(decode, s, cf);
         prepare_update_cf(decode, s, cf);
-        break;
-    default:
+    } else {
         /*
         /*
          * Z was going to be computed from the non-zero status of CC_DST.
          * Z was going to be computed from the non-zero status of CC_DST.
          * We can get that same Z value (and the new C value) by leaving
          * We can get that same Z value (and the new C value) by leaving
@@ -1473,9 +1466,8 @@ static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv
          */
          */
         decode->cc_src = tcg_temp_new();
         decode->cc_src = tcg_temp_new();
         decode->cc_dst = cpu_cc_dst;
         decode->cc_dst = cpu_cc_dst;
-        decode->cc_op = ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB;
+        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, s->T1);
-        break;
     }
     }
 }
 }
 
 
@@ -3354,7 +3346,8 @@ static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_
          * bit, we might as well fish CF out of EFLAGS and save a shift.
          * bit, we might as well fish CF out of EFLAGS and save a shift.
          */
          */
         if (want_carry && (!need_flags || s->cc_op == CC_OP_SHLB + MO_TL)) {
         if (want_carry && (!need_flags || s->cc_op == CC_OP_SHLB + MO_TL)) {
-            tcg_gen_shri_tl(decode->cc_dst, cpu_cc_src, (8 << (s->cc_op - CC_OP_SHLB)) - 1);
+            MemOp size = cc_op_size(s->cc_op);
+            tcg_gen_shri_tl(decode->cc_dst, cpu_cc_src, (8 << size) - 1);
             got_cf = true;
             got_cf = true;
         }
         }
         gen_mov_eflags(s, decode->cc_src);
         gen_mov_eflags(s, decode->cc_src);
@@ -3784,13 +3777,13 @@ static void gen_shift_dynamic_flags(DisasContext *s, X86DecodedInsn *decode, TCG
     decode->cc_op_dynamic = tcg_temp_new_i32();
     decode->cc_op_dynamic = tcg_temp_new_i32();
 
 
     assert(decode->cc_dst == s->T0);
     assert(decode->cc_dst == s->T0);
-    if (cc_op_live[s->cc_op] & USES_CC_DST) {
+    if (cc_op_live(s->cc_op) & USES_CC_DST) {
         decode->cc_dst = tcg_temp_new();
         decode->cc_dst = tcg_temp_new();
         tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0),
         tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0),
                            cpu_cc_dst, s->T0);
                            cpu_cc_dst, s->T0);
     }
     }
 
 
-    if (cc_op_live[s->cc_op] & USES_CC_SRC) {
+    if (cc_op_live(s->cc_op) & USES_CC_SRC) {
         tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src, count, tcg_constant_tl(0),
         tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src, count, tcg_constant_tl(0),
                            cpu_cc_src, decode->cc_src);
                            cpu_cc_src, decode->cc_src);
     }
     }
@@ -4724,7 +4717,8 @@ static void gen_XOR(DisasContext *s, X86DecodedInsn *decode)
         decode->op[2].unit == X86_OP_INT &&
         decode->op[2].unit == X86_OP_INT &&
         decode->op[1].n == decode->op[2].n) {
         decode->op[1].n == decode->op[2].n) {
         tcg_gen_movi_tl(s->T0, 0);
         tcg_gen_movi_tl(s->T0, 0);
-        decode->cc_op = CC_OP_CLR;
+        decode->cc_op = CC_OP_EFLAGS;
+        decode->cc_src = tcg_constant_tl(CC_Z | CC_P);
     } else {
     } else {
         MemOp ot = decode->op[1].ot;
         MemOp ot = decode->op[1].ot;
 
 

+ 5 - 1
target/i386/tcg/helper-tcg.h

@@ -21,6 +21,7 @@
 #define I386_HELPER_TCG_H
 #define I386_HELPER_TCG_H
 
 
 #include "exec/exec-all.h"
 #include "exec/exec-all.h"
+#include "qemu/host-utils.h"
 
 
 /* Maximum instruction code size */
 /* Maximum instruction code size */
 #define TARGET_MAX_INSN_SIZE 16
 #define TARGET_MAX_INSN_SIZE 16
@@ -87,7 +88,10 @@ G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
 #endif
 #endif
 
 
 /* cc_helper.c */
 /* cc_helper.c */
-extern const uint8_t parity_table[256];
+static inline unsigned int compute_pf(uint8_t x)
+{
+    return !parity8(x) * CC_P;
+}
 
 
 /* misc_helper.c */
 /* misc_helper.c */
 void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask);
 void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask);

+ 2 - 2
target/i386/tcg/int_helper.c

@@ -237,7 +237,7 @@ void helper_daa(CPUX86State *env)
     env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al;
     env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al;
     /* well, speed is not an issue here, so we compute the flags by hand */
     /* well, speed is not an issue here, so we compute the flags by hand */
     eflags |= (al == 0) << 6; /* zf */
     eflags |= (al == 0) << 6; /* zf */
-    eflags |= parity_table[al]; /* pf */
+    eflags |= compute_pf(al);
     eflags |= (al & 0x80); /* sf */
     eflags |= (al & 0x80); /* sf */
     CC_SRC = eflags;
     CC_SRC = eflags;
     CC_OP = CC_OP_EFLAGS;
     CC_OP = CC_OP_EFLAGS;
@@ -269,7 +269,7 @@ void helper_das(CPUX86State *env)
     env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al;
     env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al;
     /* well, speed is not an issue here, so we compute the flags by hand */
     /* well, speed is not an issue here, so we compute the flags by hand */
     eflags |= (al == 0) << 6; /* zf */
     eflags |= (al == 0) << 6; /* zf */
-    eflags |= parity_table[al]; /* pf */
+    eflags |= compute_pf(al);
     eflags |= (al & 0x80); /* sf */
     eflags |= (al & 0x80); /* sf */
     CC_SRC = eflags;
     CC_SRC = eflags;
     CC_OP = CC_OP_EFLAGS;
     CC_OP = CC_OP_EFLAGS;

+ 69 - 34
target/i386/tcg/translate.c

@@ -291,7 +291,7 @@ enum {
 };
 };
 
 
 /* Bit set if the global variable is live after setting CC_OP to X.  */
 /* Bit set if the global variable is live after setting CC_OP to X.  */
-static const uint8_t cc_op_live[CC_OP_NB] = {
+static const uint8_t cc_op_live_[] = {
     [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2,
     [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2,
     [CC_OP_EFLAGS] = USES_CC_SRC,
     [CC_OP_EFLAGS] = USES_CC_SRC,
     [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC,
     [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC,
@@ -309,10 +309,24 @@ static const uint8_t cc_op_live[CC_OP_NB] = {
     [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC,
     [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC,
     [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2,
     [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2,
     [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2,
     [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2,
-    [CC_OP_CLR] = 0,
     [CC_OP_POPCNT] = USES_CC_DST,
     [CC_OP_POPCNT] = USES_CC_DST,
 };
 };
 
 
+static uint8_t cc_op_live(CCOp op)
+{
+    uint8_t result;
+    assert(op >= 0 && op < ARRAY_SIZE(cc_op_live_));
+
+    /*
+     * Check that the array is fully populated.  A zero entry would correspond
+     * to a fixed value of EFLAGS, which can be obtained with CC_OP_EFLAGS
+     * as well.
+     */
+    result = cc_op_live_[op];
+    assert(result);
+    return result;
+}
+
 static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty)
 static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty)
 {
 {
     int dead;
     int dead;
@@ -322,7 +336,7 @@ static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty)
     }
     }
 
 
     /* Discard CC computation that will no longer be used.  */
     /* Discard CC computation that will no longer be used.  */
-    dead = cc_op_live[s->cc_op] & ~cc_op_live[op];
+    dead = cc_op_live(s->cc_op) & ~cc_op_live(op);
     if (dead & USES_CC_DST) {
     if (dead & USES_CC_DST) {
         tcg_gen_discard_tl(cpu_cc_dst);
         tcg_gen_discard_tl(cpu_cc_dst);
     }
     }
@@ -803,17 +817,13 @@ static void gen_mov_eflags(DisasContext *s, TCGv reg)
         tcg_gen_mov_tl(reg, cpu_cc_src);
         tcg_gen_mov_tl(reg, cpu_cc_src);
         return;
         return;
     }
     }
-    if (s->cc_op == CC_OP_CLR) {
-        tcg_gen_movi_tl(reg, CC_Z | CC_P);
-        return;
-    }
 
 
     dst = cpu_cc_dst;
     dst = cpu_cc_dst;
     src1 = cpu_cc_src;
     src1 = cpu_cc_src;
     src2 = cpu_cc_src2;
     src2 = cpu_cc_src2;
 
 
     /* Take care to not read values that are not live.  */
     /* Take care to not read values that are not live.  */
-    live = cc_op_live[s->cc_op] & ~USES_CC_SRCT;
+    live = cc_op_live(s->cc_op) & ~USES_CC_SRCT;
     dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2);
     dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2);
     if (dead) {
     if (dead) {
         TCGv zero = tcg_constant_tl(0);
         TCGv zero = tcg_constant_tl(0);
@@ -883,21 +893,20 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg)
     case CC_OP_SUBB ... CC_OP_SUBQ:
     case CC_OP_SUBB ... CC_OP_SUBQ:
         /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */
         /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */
         size = s->cc_op - CC_OP_SUBB;
         size = s->cc_op - CC_OP_SUBB;
-        gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false);
-        gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+        tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size);
+        tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size);
         return (CCPrepare) { .cond = TCG_COND_LTU, .reg = s->cc_srcT,
         return (CCPrepare) { .cond = TCG_COND_LTU, .reg = s->cc_srcT,
                              .reg2 = cpu_cc_src, .use_reg2 = true };
                              .reg2 = cpu_cc_src, .use_reg2 = true };
 
 
     case CC_OP_ADDB ... CC_OP_ADDQ:
     case CC_OP_ADDB ... CC_OP_ADDQ:
         /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */
         /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */
-        size = s->cc_op - CC_OP_ADDB;
-        gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size, false);
-        gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+        size = cc_op_size(s->cc_op);
+        tcg_gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size);
+        tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size);
         return (CCPrepare) { .cond = TCG_COND_LTU, .reg = cpu_cc_dst,
         return (CCPrepare) { .cond = TCG_COND_LTU, .reg = cpu_cc_dst,
                              .reg2 = cpu_cc_src, .use_reg2 = true };
                              .reg2 = cpu_cc_src, .use_reg2 = true };
 
 
     case CC_OP_LOGICB ... CC_OP_LOGICQ:
     case CC_OP_LOGICB ... CC_OP_LOGICQ:
-    case CC_OP_CLR:
     case CC_OP_POPCNT:
     case CC_OP_POPCNT:
         return (CCPrepare) { .cond = TCG_COND_NEVER };
         return (CCPrepare) { .cond = TCG_COND_NEVER };
 
 
@@ -908,7 +917,7 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg)
 
 
     case CC_OP_SHLB ... CC_OP_SHLQ:
     case CC_OP_SHLB ... CC_OP_SHLQ:
         /* (CC_SRC >> (DATA_BITS - 1)) & 1 */
         /* (CC_SRC >> (DATA_BITS - 1)) & 1 */
-        size = s->cc_op - CC_OP_SHLB;
+        size = cc_op_size(s->cc_op);
         return gen_prepare_sign_nz(cpu_cc_src, size);
         return gen_prepare_sign_nz(cpu_cc_src, size);
 
 
     case CC_OP_MULB ... CC_OP_MULQ:
     case CC_OP_MULB ... CC_OP_MULQ:
@@ -916,11 +925,11 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg)
                              .reg = cpu_cc_src };
                              .reg = cpu_cc_src };
 
 
     case CC_OP_BMILGB ... CC_OP_BMILGQ:
     case CC_OP_BMILGB ... CC_OP_BMILGQ:
-        size = s->cc_op - CC_OP_BMILGB;
+        size = cc_op_size(s->cc_op);
         return gen_prepare_val_nz(cpu_cc_src, size, true);
         return gen_prepare_val_nz(cpu_cc_src, size, true);
 
 
     case CC_OP_BLSIB ... CC_OP_BLSIQ:
     case CC_OP_BLSIB ... CC_OP_BLSIQ:
-        size = s->cc_op - CC_OP_BLSIB;
+        size = cc_op_size(s->cc_op);
         return gen_prepare_val_nz(cpu_cc_src, size, false);
         return gen_prepare_val_nz(cpu_cc_src, size, false);
 
 
     case CC_OP_ADCX:
     case CC_OP_ADCX:
@@ -969,14 +978,10 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg)
     case CC_OP_ADCOX:
     case CC_OP_ADCOX:
         return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
         return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
                              .imm = CC_S };
                              .imm = CC_S };
-    case CC_OP_CLR:
     case CC_OP_POPCNT:
     case CC_OP_POPCNT:
         return (CCPrepare) { .cond = TCG_COND_NEVER };
         return (CCPrepare) { .cond = TCG_COND_NEVER };
     default:
     default:
-        {
-            MemOp size = (s->cc_op - CC_OP_ADDB) & 3;
-            return gen_prepare_sign_nz(cpu_cc_dst, size);
-        }
+        return gen_prepare_sign_nz(cpu_cc_dst, cc_op_size(s->cc_op));
     }
     }
 }
 }
 
 
@@ -988,7 +993,7 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg)
     case CC_OP_ADCOX:
     case CC_OP_ADCOX:
         return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2,
         return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2,
                              .no_setcond = true };
                              .no_setcond = true };
-    case CC_OP_CLR:
+    case CC_OP_LOGICB ... CC_OP_LOGICQ:
     case CC_OP_POPCNT:
     case CC_OP_POPCNT:
         return (CCPrepare) { .cond = TCG_COND_NEVER };
         return (CCPrepare) { .cond = TCG_COND_NEVER };
     case CC_OP_MULB ... CC_OP_MULQ:
     case CC_OP_MULB ... CC_OP_MULQ:
@@ -1004,20 +1009,24 @@ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg)
 static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
 static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
 {
 {
     switch (s->cc_op) {
     switch (s->cc_op) {
-    case CC_OP_DYNAMIC:
-        gen_compute_eflags(s);
-        /* FALLTHRU */
     case CC_OP_EFLAGS:
     case CC_OP_EFLAGS:
     case CC_OP_ADCX:
     case CC_OP_ADCX:
     case CC_OP_ADOX:
     case CC_OP_ADOX:
     case CC_OP_ADCOX:
     case CC_OP_ADCOX:
         return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
         return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
                              .imm = CC_Z };
                              .imm = CC_Z };
-    case CC_OP_CLR:
-        return (CCPrepare) { .cond = TCG_COND_ALWAYS };
+    case CC_OP_DYNAMIC:
+        gen_update_cc_op(s);
+        if (!reg) {
+            reg = tcg_temp_new();
+        }
+        gen_helper_cc_compute_nz(reg, cpu_cc_dst, cpu_cc_src, cpu_cc_op);
+        return (CCPrepare) { .cond = TCG_COND_EQ, .reg = reg, .imm = 0 };
+    case CC_OP_POPCNT:
+        return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst };
     default:
     default:
         {
         {
-            MemOp size = (s->cc_op - CC_OP_ADDB) & 3;
+            MemOp size = cc_op_size(s->cc_op);
             return gen_prepare_val_nz(cpu_cc_dst, size, true);
             return gen_prepare_val_nz(cpu_cc_dst, size, true);
         }
         }
     }
     }
@@ -1038,11 +1047,11 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
     switch (s->cc_op) {
     switch (s->cc_op) {
     case CC_OP_SUBB ... CC_OP_SUBQ:
     case CC_OP_SUBB ... CC_OP_SUBQ:
         /* We optimize relational operators for the cmp/jcc case.  */
         /* We optimize relational operators for the cmp/jcc case.  */
-        size = s->cc_op - CC_OP_SUBB;
+        size = cc_op_size(s->cc_op);
         switch (jcc_op) {
         switch (jcc_op) {
         case JCC_BE:
         case JCC_BE:
-            gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false);
-            gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+            tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size);
+            tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size);
             cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->cc_srcT,
             cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->cc_srcT,
                                .reg2 = cpu_cc_src, .use_reg2 = true };
                                .reg2 = cpu_cc_src, .use_reg2 = true };
             break;
             break;
@@ -1052,8 +1061,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
         case JCC_LE:
         case JCC_LE:
             cond = TCG_COND_LE;
             cond = TCG_COND_LE;
         fast_jcc_l:
         fast_jcc_l:
-            gen_ext_tl(s->cc_srcT, s->cc_srcT, size, true);
-            gen_ext_tl(cpu_cc_src, cpu_cc_src, size, true);
+            tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size | MO_SIGN);
+            tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size | MO_SIGN);
             cc = (CCPrepare) { .cond = cond, .reg = s->cc_srcT,
             cc = (CCPrepare) { .cond = cond, .reg = s->cc_srcT,
                                .reg2 = cpu_cc_src, .use_reg2 = true };
                                .reg2 = cpu_cc_src, .use_reg2 = true };
             break;
             break;
@@ -1063,6 +1072,28 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
         }
         }
         break;
         break;
 
 
+    case CC_OP_LOGICB ... CC_OP_LOGICQ:
+        /* Mostly used for test+jump */
+        size = s->cc_op - CC_OP_LOGICB;
+        switch (jcc_op) {
+        case JCC_BE:
+            /* CF = 0, becomes jz/je */
+            jcc_op = JCC_Z;
+            goto slow_jcc;
+        case JCC_L:
+            /* OF = 0, becomes js/jns */
+            jcc_op = JCC_S;
+            goto slow_jcc;
+        case JCC_LE:
+            /* SF or ZF, becomes signed <= 0 */
+            tcg_gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size | MO_SIGN);
+            cc = (CCPrepare) { .cond = TCG_COND_LE, .reg = cpu_cc_dst };
+            break;
+        default:
+            goto slow_jcc;
+        }
+        break;
+
     default:
     default:
     slow_jcc:
     slow_jcc:
         /* This actually generates good code for JC, JZ and JS.  */
         /* This actually generates good code for JC, JZ and JS.  */
@@ -1162,6 +1193,10 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1)
 {
 {
     CCPrepare cc = gen_prepare_cc(s, b, NULL);
     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!
+     */
     gen_update_cc_op(s);
     gen_update_cc_op(s);
     if (cc.use_reg2) {
     if (cc.use_reg2) {
         tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1);
         tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1);

+ 3 - 0
tests/docker/dockerfiles/alpine.docker

@@ -45,6 +45,7 @@ RUN apk update && \
         libaio-dev \
         libaio-dev \
         libbpf-dev \
         libbpf-dev \
         libcap-ng-dev \
         libcap-ng-dev \
+        libcbor-dev \
         libdrm-dev \
         libdrm-dev \
         libepoxy-dev \
         libepoxy-dev \
         libffi-dev \
         libffi-dev \
@@ -90,6 +91,8 @@ RUN apk update && \
         py3-yaml \
         py3-yaml \
         python3 \
         python3 \
         rpm2cpio \
         rpm2cpio \
+        rust \
+        rust-bindgen \
         samurai \
         samurai \
         sdl2-dev \
         sdl2-dev \
         sdl2_image-dev \
         sdl2_image-dev \

+ 2 - 0
tests/docker/dockerfiles/centos9.docker

@@ -16,6 +16,7 @@ RUN dnf distro-sync -y && \
         alsa-lib-devel \
         alsa-lib-devel \
         bash \
         bash \
         bc \
         bc \
+        bindgen-cli \
         bison \
         bison \
         brlapi-devel \
         brlapi-devel \
         bzip2 \
         bzip2 \
@@ -102,6 +103,7 @@ RUN dnf distro-sync -y && \
         python3-sphinx_rtd_theme \
         python3-sphinx_rtd_theme \
         python3-tomli \
         python3-tomli \
         rdma-core-devel \
         rdma-core-devel \
+        rust \
         sed \
         sed \
         snappy-devel \
         snappy-devel \
         socat \
         socat \

+ 4 - 0
tests/docker/dockerfiles/debian-amd64-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:amd64 \
                       libcacard-dev:amd64 \
                       libcap-ng-dev:amd64 \
                       libcap-ng-dev:amd64 \
                       libcapstone-dev:amd64 \
                       libcapstone-dev:amd64 \
+                      libcbor-dev:amd64 \
                       libcmocka-dev:amd64 \
                       libcmocka-dev:amd64 \
                       libcurl4-gnutls-dev:amd64 \
                       libcurl4-gnutls-dev:amd64 \
                       libdaxctl-dev:amd64 \
                       libdaxctl-dev:amd64 \
@@ -170,6 +173,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \
 
 
 ENV ABI "x86_64-linux-gnu"
 ENV ABI "x86_64-linux-gnu"
 ENV MESON_OPTS "--cross-file=x86_64-linux-gnu"
 ENV MESON_OPTS "--cross-file=x86_64-linux-gnu"
+ENV RUST_TARGET "x86_64-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu-
 ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user
 ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-arm64-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:arm64 \
                       libcacard-dev:arm64 \
                       libcap-ng-dev:arm64 \
                       libcap-ng-dev:arm64 \
                       libcapstone-dev:arm64 \
                       libcapstone-dev:arm64 \
+                      libcbor-dev:arm64 \
                       libcmocka-dev:arm64 \
                       libcmocka-dev:arm64 \
                       libcurl4-gnutls-dev:arm64 \
                       libcurl4-gnutls-dev:arm64 \
                       libdaxctl-dev:arm64 \
                       libdaxctl-dev:arm64 \
@@ -169,6 +172,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/aarch64-linux-gnu && \
 
 
 ENV ABI "aarch64-linux-gnu"
 ENV ABI "aarch64-linux-gnu"
 ENV MESON_OPTS "--cross-file=aarch64-linux-gnu"
 ENV MESON_OPTS "--cross-file=aarch64-linux-gnu"
+ENV RUST_TARGET "aarch64-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=aarch64-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=aarch64-linux-gnu-
 ENV DEF_TARGET_LIST aarch64-softmmu,aarch64-linux-user
 ENV DEF_TARGET_LIST aarch64-softmmu,aarch64-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-armhf-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:armhf \
                       libcacard-dev:armhf \
                       libcap-ng-dev:armhf \
                       libcap-ng-dev:armhf \
                       libcapstone-dev:armhf \
                       libcapstone-dev:armhf \
+                      libcbor-dev:armhf \
                       libcmocka-dev:armhf \
                       libcmocka-dev:armhf \
                       libcurl4-gnutls-dev:armhf \
                       libcurl4-gnutls-dev:armhf \
                       libdaxctl-dev:armhf \
                       libdaxctl-dev:armhf \
@@ -169,6 +172,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \
 
 
 ENV ABI "arm-linux-gnueabihf"
 ENV ABI "arm-linux-gnueabihf"
 ENV MESON_OPTS "--cross-file=arm-linux-gnueabihf"
 ENV MESON_OPTS "--cross-file=arm-linux-gnueabihf"
+ENV RUST_TARGET "armv7-unknown-linux-gnueabihf"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabihf-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabihf-
 ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user
 ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-i686-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:i386 \
                       libcacard-dev:i386 \
                       libcap-ng-dev:i386 \
                       libcap-ng-dev:i386 \
                       libcapstone-dev:i386 \
                       libcapstone-dev:i386 \
+                      libcbor-dev:i386 \
                       libcmocka-dev:i386 \
                       libcmocka-dev:i386 \
                       libcurl4-gnutls-dev:i386 \
                       libcurl4-gnutls-dev:i386 \
                       libdaxctl-dev:i386 \
                       libdaxctl-dev:i386 \
@@ -168,6 +171,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \
 
 
 ENV ABI "i686-linux-gnu"
 ENV ABI "i686-linux-gnu"
 ENV MESON_OPTS "--cross-file=i686-linux-gnu"
 ENV MESON_OPTS "--cross-file=i686-linux-gnu"
+ENV RUST_TARGET "i686-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-linux-gnu-
 ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user
 ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-mips64el-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -91,6 +93,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:mips64el \
                       libcacard-dev:mips64el \
                       libcap-ng-dev:mips64el \
                       libcap-ng-dev:mips64el \
                       libcapstone-dev:mips64el \
                       libcapstone-dev:mips64el \
+                      libcbor-dev:mips64el \
                       libcmocka-dev:mips64el \
                       libcmocka-dev:mips64el \
                       libcurl4-gnutls-dev:mips64el \
                       libcurl4-gnutls-dev:mips64el \
                       libdaxctl-dev:mips64el \
                       libdaxctl-dev:mips64el \
@@ -158,6 +161,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \
 
 
 ENV ABI "mips64el-linux-gnuabi64"
 ENV ABI "mips64el-linux-gnuabi64"
 ENV MESON_OPTS "--cross-file=mips64el-linux-gnuabi64"
 ENV MESON_OPTS "--cross-file=mips64el-linux-gnuabi64"
+ENV RUST_TARGET "mips64el-unknown-linux-gnuabi64"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=mips64el-linux-gnuabi64-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=mips64el-linux-gnuabi64-
 ENV DEF_TARGET_LIST mips64el-softmmu,mips64el-linux-user
 ENV DEF_TARGET_LIST mips64el-softmmu,mips64el-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-mipsel-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -91,6 +93,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:mipsel \
                       libcacard-dev:mipsel \
                       libcap-ng-dev:mipsel \
                       libcap-ng-dev:mipsel \
                       libcapstone-dev:mipsel \
                       libcapstone-dev:mipsel \
+                      libcbor-dev:mipsel \
                       libcmocka-dev:mipsel \
                       libcmocka-dev:mipsel \
                       libcurl4-gnutls-dev:mipsel \
                       libcurl4-gnutls-dev:mipsel \
                       libdaxctl-dev:mipsel \
                       libdaxctl-dev:mipsel \
@@ -166,6 +169,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/mipsel-linux-gnu && \
 
 
 ENV ABI "mipsel-linux-gnu"
 ENV ABI "mipsel-linux-gnu"
 ENV MESON_OPTS "--cross-file=mipsel-linux-gnu"
 ENV MESON_OPTS "--cross-file=mipsel-linux-gnu"
+ENV RUST_TARGET "mipsel-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=mipsel-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=mipsel-linux-gnu-
 ENV DEF_TARGET_LIST mipsel-softmmu,mipsel-linux-user
 ENV DEF_TARGET_LIST mipsel-softmmu,mipsel-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-ppc64el-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:ppc64el \
                       libcacard-dev:ppc64el \
                       libcap-ng-dev:ppc64el \
                       libcap-ng-dev:ppc64el \
                       libcapstone-dev:ppc64el \
                       libcapstone-dev:ppc64el \
+                      libcbor-dev:ppc64el \
                       libcmocka-dev:ppc64el \
                       libcmocka-dev:ppc64el \
                       libcurl4-gnutls-dev:ppc64el \
                       libcurl4-gnutls-dev:ppc64el \
                       libdaxctl-dev:ppc64el \
                       libdaxctl-dev:ppc64el \
@@ -168,6 +171,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \
 
 
 ENV ABI "powerpc64le-linux-gnu"
 ENV ABI "powerpc64le-linux-gnu"
 ENV MESON_OPTS "--cross-file=powerpc64le-linux-gnu"
 ENV MESON_OPTS "--cross-file=powerpc64le-linux-gnu"
+ENV RUST_TARGET "powerpc64le-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc64le-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc64le-linux-gnu-
 ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user
 ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 4 - 0
tests/docker/dockerfiles/debian-s390x-cross.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -53,6 +54,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \
@@ -92,6 +94,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev:s390x \
                       libcacard-dev:s390x \
                       libcap-ng-dev:s390x \
                       libcap-ng-dev:s390x \
                       libcapstone-dev:s390x \
                       libcapstone-dev:s390x \
+                      libcbor-dev:s390x \
                       libcmocka-dev:s390x \
                       libcmocka-dev:s390x \
                       libcurl4-gnutls-dev:s390x \
                       libcurl4-gnutls-dev:s390x \
                       libdaxctl-dev:s390x \
                       libdaxctl-dev:s390x \
@@ -167,6 +170,7 @@ endian = 'big'\n" > /usr/local/share/meson/cross/s390x-linux-gnu && \
 
 
 ENV ABI "s390x-linux-gnu"
 ENV ABI "s390x-linux-gnu"
 ENV MESON_OPTS "--cross-file=s390x-linux-gnu"
 ENV MESON_OPTS "--cross-file=s390x-linux-gnu"
+ENV RUST_TARGET "s390x-unknown-linux-gnu"
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=s390x-linux-gnu-
 ENV QEMU_CONFIGURE_OPTS --cross-prefix=s390x-linux-gnu-
 ENV DEF_TARGET_LIST s390x-softmmu,s390x-linux-user
 ENV DEF_TARGET_LIST s390x-softmmu,s390x-linux-user
 # As a final step configure the user (if env is defined)
 # As a final step configure the user (if env is defined)

+ 3 - 0
tests/docker/dockerfiles/debian.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -41,6 +42,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev \
                       libcacard-dev \
                       libcap-ng-dev \
                       libcap-ng-dev \
                       libcapstone-dev \
                       libcapstone-dev \
+                      libcbor-dev \
                       libcmocka-dev \
                       libcmocka-dev \
                       libcurl4-gnutls-dev \
                       libcurl4-gnutls-dev \
                       libdaxctl-dev \
                       libdaxctl-dev \
@@ -120,6 +122,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \

+ 3 - 0
tests/docker/dockerfiles/fedora-rust-nightly.docker

@@ -23,6 +23,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                alsa-lib-devel \
                alsa-lib-devel \
                bash \
                bash \
                bc \
                bc \
+               bindgen-cli \
                bison \
                bison \
                brlapi-devel \
                brlapi-devel \
                bzip2 \
                bzip2 \
@@ -61,6 +62,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                libbpf-devel \
                libbpf-devel \
                libcacard-devel \
                libcacard-devel \
                libcap-ng-devel \
                libcap-ng-devel \
+               libcbor-devel \
                libcmocka-devel \
                libcmocka-devel \
                libcurl-devel \
                libcurl-devel \
                libdrm-devel \
                libdrm-devel \
@@ -113,6 +115,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                python3-sphinx_rtd_theme \
                python3-sphinx_rtd_theme \
                python3-zombie-imp \
                python3-zombie-imp \
                rdma-core-devel \
                rdma-core-devel \
+               rust \
                sed \
                sed \
                snappy-devel \
                snappy-devel \
                socat \
                socat \

+ 2 - 0
tests/docker/dockerfiles/fedora-win64-cross.docker

@@ -20,6 +20,7 @@ exec "$@"\n' > /usr/bin/nosync && \
     nosync dnf install -y \
     nosync dnf install -y \
                bash \
                bash \
                bc \
                bc \
+               bindgen-cli \
                bison \
                bison \
                bzip2 \
                bzip2 \
                ca-certificates \
                ca-certificates \
@@ -53,6 +54,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                python3-sphinx \
                python3-sphinx \
                python3-sphinx_rtd_theme \
                python3-sphinx_rtd_theme \
                python3-zombie-imp \
                python3-zombie-imp \
+               rust \
                sed \
                sed \
                socat \
                socat \
                sparse \
                sparse \

+ 3 - 0
tests/docker/dockerfiles/fedora.docker

@@ -23,6 +23,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                alsa-lib-devel \
                alsa-lib-devel \
                bash \
                bash \
                bc \
                bc \
+               bindgen-cli \
                bison \
                bison \
                brlapi-devel \
                brlapi-devel \
                bzip2 \
                bzip2 \
@@ -61,6 +62,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                libbpf-devel \
                libbpf-devel \
                libcacard-devel \
                libcacard-devel \
                libcap-ng-devel \
                libcap-ng-devel \
+               libcbor-devel \
                libcmocka-devel \
                libcmocka-devel \
                libcurl-devel \
                libcurl-devel \
                libdrm-devel \
                libdrm-devel \
@@ -113,6 +115,7 @@ exec "$@"\n' > /usr/bin/nosync && \
                python3-sphinx_rtd_theme \
                python3-sphinx_rtd_theme \
                python3-zombie-imp \
                python3-zombie-imp \
                rdma-core-devel \
                rdma-core-devel \
+               rust \
                sed \
                sed \
                snappy-devel \
                snappy-devel \
                socat \
                socat \

+ 3 - 0
tests/docker/dockerfiles/opensuse-leap.docker

@@ -47,6 +47,7 @@ RUN zypper update -y && \
            libbz2-devel \
            libbz2-devel \
            libcacard-devel \
            libcacard-devel \
            libcap-ng-devel \
            libcap-ng-devel \
+           libcbor-devel \
            libcmocka-devel \
            libcmocka-devel \
            libcurl-devel \
            libcurl-devel \
            libdrm-devel \
            libdrm-devel \
@@ -96,6 +97,8 @@ RUN zypper update -y && \
            python311-pip \
            python311-pip \
            python311-setuptools \
            python311-setuptools \
            rdma-core-devel \
            rdma-core-devel \
+           rust \
+           rust-bindgen \
            sed \
            sed \
            snappy-devel \
            snappy-devel \
            sndio-devel \
            sndio-devel \

+ 3 - 0
tests/docker/dockerfiles/ubuntu2204.docker

@@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     eatmydata apt-get install --no-install-recommends -y \
     eatmydata apt-get install --no-install-recommends -y \
                       bash \
                       bash \
                       bc \
                       bc \
+                      bindgen \
                       bison \
                       bison \
                       bsdextrautils \
                       bsdextrautils \
                       bzip2 \
                       bzip2 \
@@ -41,6 +42,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       libcacard-dev \
                       libcacard-dev \
                       libcap-ng-dev \
                       libcap-ng-dev \
                       libcapstone-dev \
                       libcapstone-dev \
+                      libcbor-dev \
                       libcmocka-dev \
                       libcmocka-dev \
                       libcurl4-gnutls-dev \
                       libcurl4-gnutls-dev \
                       libdaxctl-dev \
                       libdaxctl-dev \
@@ -120,6 +122,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
                       python3-venv \
                       python3-venv \
                       python3-yaml \
                       python3-yaml \
                       rpm2cpio \
                       rpm2cpio \
+                      rustc \
                       sed \
                       sed \
                       socat \
                       socat \
                       sparse \
                       sparse \

+ 3 - 0
tests/lcitool/projects/qemu.yml

@@ -3,6 +3,7 @@ packages:
  - alsa
  - alsa
  - bash
  - bash
  - bc
  - bc
+ - bindgen
  - bison
  - bison
  - brlapi
  - brlapi
  - bzip2
  - bzip2
@@ -42,6 +43,7 @@ packages:
  - libc-static
  - libc-static
  - libcacard
  - libcacard
  - libcap-ng
  - libcap-ng
+ - libcbor
  - libcurl
  - libcurl
  - libdrm
  - libdrm
  - libepoxy
  - libepoxy
@@ -101,6 +103,7 @@ packages:
  - python3-tomli
  - python3-tomli
  - python3-venv
  - python3-venv
  - rpm2cpio
  - rpm2cpio
+ - rust
  - sdl2
  - sdl2
  - sdl2-image
  - sdl2-image
  - sed
  - sed

+ 2 - 1
tests/qtest/libqtest.c

@@ -1648,7 +1648,8 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
         /* Ignore machines that cannot be used for qtests */
         /* Ignore machines that cannot be used for qtests */
         if (!strncmp("xenfv", machines[i].name, 5) ||
         if (!strncmp("xenfv", machines[i].name, 5) ||
             g_str_equal("xenpv", machines[i].name) ||
             g_str_equal("xenpv", machines[i].name) ||
-            g_str_equal("xenpvh", machines[i].name)) {
+            g_str_equal("xenpvh", machines[i].name) ||
+            g_str_equal("nitro-enclave", machines[i].name)) {
             continue;
             continue;
         }
         }
         if (!skip_old_versioned ||
         if (!skip_old_versioned ||

+ 2 - 0
tests/vm/generated/freebsd.json

@@ -61,6 +61,8 @@
     "py311-tomli",
     "py311-tomli",
     "python3",
     "python3",
     "rpm2cpio",
     "rpm2cpio",
+    "rust",
+    "rust-bindgen-cli",
     "sdl2",
     "sdl2",
     "sdl2_image",
     "sdl2_image",
     "snappy",
     "snappy",

+ 1 - 0
util/cpuinfo-i386.c

@@ -35,6 +35,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void)
         __cpuid(1, a, b, c, d);
         __cpuid(1, a, b, c, d);
 
 
         info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0);
         info |= (d & bit_SSE2 ? CPUINFO_SSE2 : 0);
+        info |= (c & bit_OSXSAVE ? CPUINFO_OSXSAVE : 0);
         info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0);
         info |= (c & bit_MOVBE ? CPUINFO_MOVBE : 0);
         info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0);
         info |= (c & bit_POPCNT ? CPUINFO_POPCNT : 0);
         info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0);
         info |= (c & bit_PCLMUL ? CPUINFO_PCLMUL : 0);