diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-03 10:20:59 +0800 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-03 10:20:59 +0800 |
commit | 8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0 (patch) | |
tree | 6059933b4c0205cdfb5f597f94a2ad2d0b4debfd | |
parent | 70fc2bde913ef8a6b77ded6831534d9c79541e17 (diff) | |
parent | a4e749780bd20593c0c386612a51bf4d64a80132 (diff) | |
download | qemu-8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0.zip qemu-8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0.tar.gz qemu-8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0.tar.bz2 |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* qom: Use command line syntax for default values in help
* i386: support cache topology with machine's configuration
* rust: fix duplicate symbols from monitor-fd.c
* rust: add module to convert between success/-errno and io::Result
* rust: move class_init implementation from trait to method
* pvg: configuration improvements
* kvm guestmemfd: replace assertion with error
* riscv: cleanups
* target/i386/hvf: cleanups to emulation
* target/i386: add Zhaoxin and Yongfeng CPU model
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAme+10sUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroMkRwf/eT0gVbE3u0TS6EVZwjGZPHEOEyy/
# gl39SlTT97HxoAClE4PRcdkn7YR3f30hytHghc4qhou+Eh/7Mj2Ox7l7+CyaaCS/
# fxowsOVMBV7++PkyKRPxIMamKzD8Bo0eGwWe+CJijA0zt9PSI/YEwRV0pf/s6KCW
# pOya2f+aNbAo3O5RWtIKSISgbSVvuVzDcDHyfydmOHuvGr2NHAM8UfZYD+41qy5B
# 81PYlvK6HgvhaCboqCUADULkte96Xmc4p2ggk0ZNiy0ho46rs78SMyBh5sXR2S3I
# moiQHpJXyV5TcI7HmwvcW7s0/cpdKm/wmPOjb6otu9InWh/ON1nnURsTEQ==
# =V/fm
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 26 Feb 2025 16:56:43 HKT
# 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' of https://gitlab.com/bonzini/qemu: (34 commits)
target/i386: Mask CMPLegacy bit in CPUID[0x80000001].ECX for Zhaoxin CPUs
target/i386: Introduce Zhaoxin Yongfeng CPU model
target/i386: Add CPUID leaf 0xC000_0001 EDX definitions
target/i386: Add support for Zhaoxin CPU vendor identification
target/riscv: move 128-bit check to TCG realize
target/riscv: remove unused macro DEFINE_CPU
i386/cpu: add has_caches flag to check smp_cache configuration
i386/pc: Support cache topology in -machine for PC machine
i386/cpu: Update cache topology with machine's configuration
i386/cpu: Support module level cache topology
rust: qom: get rid of ClassInitImpl
rust: pl011, qemu_api tests: do not use ClassInitImpl
rust: qom: add ObjectImpl::CLASS_INIT
rust: add SysBusDeviceImpl
rust: add IsA bounds to QOM implementation traits
target/i386/hvf: drop some dead code
target/i386/hvf: move and rename simulate_{rdmsr, wrmsr}
target/i386/hvf: move and rename {load, store}_regs
target/i386/hvf: use x86_segment in x86_decode.c
target/i386/hvf: fix the declaration of hvf_handle_io
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
53 files changed, 1275 insertions, 579 deletions
diff --git a/Kconfig.host b/Kconfig.host index 842cbe0..933425c 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -61,3 +61,6 @@ config HV_BALLOON_POSSIBLE config HAVE_RUST bool + +config MAC_PVG + bool diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 90958e5..5d8aa3a 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -139,16 +139,22 @@ anymore. Writing Rust code in QEMU ------------------------- -Right now QEMU includes three crates: +QEMU includes four crates: * ``qemu_api`` for bindings to C code and useful functionality * ``qemu_api_macros`` defines several procedural macros that are useful when writing C code -* ``pl011`` (under ``rust/hw/char/pl011``) is the sample device that is being - used to further develop ``qemu_api`` and ``qemu_api_macros``. It is a functional - replacement for the ``hw/char/pl011.c`` file. +* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) + are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are + used to further develop them. These two crates are functional\ [#issues]_ replacements + for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. + +.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` + as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of + commit f32352ff9e. Both are lacking tracing functionality; ``hpet`` + is also lacking support for migration. This section explains how to work with them. @@ -179,6 +185,7 @@ module status ``callbacks`` complete ``cell`` stable ``c_str`` complete +``errno`` complete ``irq`` complete ``memory`` stable ``module`` complete @@ -293,7 +300,7 @@ to a Rust mutable reference, and use a shared reference instead. Rust code will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which enforce that locking rules for the "Big QEMU Lock" are respected. These cell types are also known to the ``vmstate`` crate, which is able to "look inside" -them when building an in-memory representation of a ``struct``s layout. +them when building an in-memory representation of a ``struct``'s layout. Note that the same is not true of a ``RefCell`` or ``Mutex``. In the future, similar cell types might also be provided for ``AioContext``-based @@ -349,7 +356,7 @@ Writing procedural macros ''''''''''''''''''''''''' By conventions, procedural macros are split in two functions, one -returning ``Result<proc_macro2::TokenStream, MacroError>` with the body of +returning ``Result<proc_macro2::TokenStream, MacroError>`` with the body of the procedural macro, and the second returning ``proc_macro::TokenStream`` which is the actual procedural macro. The former's name is the same as the latter with the ``_or_error`` suffix. The code for the latter is more diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 4e020c3..0be0ac0 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -332,6 +332,8 @@ bool machine_parse_smp_cache(MachineState *ms, return false; } } + + mc->smp_props.has_caches = true; return true; } diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 2b53dfd..1e95ab2 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -141,10 +141,6 @@ config XLNX_DISPLAYPORT config DM163 bool -config MAC_PVG - bool - default y - config MAC_PVG_MMIO bool depends on MAC_PVG && AARCH64 diff --git a/hw/display/meson.build b/hw/display/meson.build index 94f4f05..90e6c04 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -61,13 +61,8 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) -if host_os == 'darwin' - system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) - system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) - if cpu == 'aarch64' - system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) - endif -endif +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('apple-gfx.m', 'apple-gfx-pci.m')]) +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m')]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f199a8c..63a96cd 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1798,6 +1798,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->nvdimm_supported = true; mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1D] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1I] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L2] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L3] = true; mc->default_ram_id = "pc.ram"; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index dcff18a..ccb97b6 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -77,6 +77,7 @@ struct HPETState { uint8_t rtc_irq_level; qemu_irq pit_enabled; uint8_t num_timers; + uint8_t num_timers_save; uint32_t intcap; HPETTimer timer[HPET_MAX_TIMERS]; @@ -237,15 +238,12 @@ static int hpet_pre_save(void *opaque) s->hpet_counter = hpet_get_ticks(s); } - return 0; -} - -static int hpet_pre_load(void *opaque) -{ - HPETState *s = opaque; - - /* version 1 only supports 3, later versions will load the actual value */ - s->num_timers = HPET_MIN_TIMERS; + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + s->num_timers_save = s->num_timers; return 0; } @@ -253,12 +251,7 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) { HPETState *s = opaque; - if (s->num_timers < HPET_MIN_TIMERS) { - return false; - } else if (s->num_timers > HPET_MAX_TIMERS) { - return false; - } - return true; + return s->num_timers == s->num_timers_save; } static int hpet_post_load(void *opaque, int version_id) @@ -277,16 +270,6 @@ static int hpet_post_load(void *opaque, int version_id) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } - /* Push number of timers into capability returned via HPET_ID */ - s->capability &= ~HPET_ID_NUM_TIM_MASK; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - - /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ - s->flags &= ~(1 << HPET_MSI_SUPPORT); - if (s->timer[0].config & HPET_TN_FSB_CAP) { - s->flags |= 1 << HPET_MSI_SUPPORT; - } return 0; } @@ -347,14 +330,13 @@ static const VMStateDescription vmstate_hpet = { .version_id = 2, .minimum_version_id = 1, .pre_save = hpet_pre_save, - .pre_load = hpet_pre_load, .post_load = hpet_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_UINT8_V(num_timers, HPETState, 2), - VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), + VMSTATE_UINT8_V(num_timers_save, HPETState, 2), + VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers), VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() diff --git a/include/hw/boards.h b/include/hw/boards.h index 9360d1c..f22b2e7 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -156,6 +156,8 @@ typedef struct { * @modules_supported - whether modules are supported by the machine * @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration are * supported by the machine + * @has_caches - whether cache properties are explicitly specified in the + * user provided smp-cache configuration */ typedef struct { bool prefer_sockets; @@ -166,6 +168,7 @@ typedef struct { bool drawers_supported; bool modules_supported; bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX]; + bool has_caches; } SMPCompatProps; /** diff --git a/meson.build b/meson.build index 0ee79c6..0a2c61d 100644 --- a/meson.build +++ b/meson.build @@ -821,7 +821,6 @@ version_res = [] coref = [] iokit = [] pvg = not_found -metal = [] emulator_link_args = [] midl = not_found widl = not_found @@ -843,8 +842,8 @@ elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' - pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') - metal = dependency('appleframeworks', modules: 'Metal') + pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], + required: get_option('pvg')) elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), @@ -3367,6 +3366,12 @@ foreach target : target_dirs target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y' target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN'] + # PVG is not cross-architecture. Use accelerator_targets as a proxy to + # figure out which target can support PVG on this host + if pvg.found() and target in accelerator_targets.get('CONFIG_HVF', []) + target_kconfig += 'CONFIG_MAC_PVG=y' + endif + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( @@ -4840,6 +4845,9 @@ summary_info += {'libdw': libdw} if host_os == 'freebsd' summary_info += {'libinotify-kqueue': inotify} endif +if host_os == 'darwin' + summary_info += {'ParavirtualizedGraphics support': pvg} +endif summary(summary_info, bool_yn: true, section: 'Dependencies') if host_arch == 'unknown' diff --git a/meson_options.txt b/meson_options.txt index 5eeaf3e..59d973b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -198,6 +198,8 @@ option('lzfse', type : 'feature', value : 'auto', description: 'lzfse support for DMG images') option('lzo', type : 'feature', value : 'auto', description: 'lzo compression support') +option('pvg', type: 'feature', value: 'auto', + description: 'macOS paravirtualized graphics support') option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') option('opengl', type : 'feature', value : 'auto', diff --git a/qemu-options.hx b/qemu-options.hx index 61270e3..dc694a9 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -42,7 +42,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" #endif " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" - " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", + " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n" + " smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel\n", QEMU_ARCH_ALL) SRST ``-machine [type=]name[,prop=value[,...]]`` @@ -172,6 +173,33 @@ SRST :: -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512 + + ``smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel`` + Define cache properties for SMP system. + + ``cache=cachename`` specifies the cache that the properties will be + applied on. This field is the combination of cache level and cache + type. It supports ``l1d`` (L1 data cache), ``l1i`` (L1 instruction + cache), ``l2`` (L2 unified cache) and ``l3`` (L3 unified cache). + + ``topology=topologylevel`` sets the cache topology level. It accepts + CPU topology levels including ``core``, ``module``, ``cluster``, ``die``, + ``socket``, ``book``, ``drawer`` and a special value ``default``. If + ``default`` is set, then the cache topology will follow the architecture's + default cache topology model. If another topology level is set, the cache + will be shared at corresponding CPU topology level. For example, + ``topology=core`` makes the cache shared by all threads within a core. + The omitting cache will default to using the ``default`` level. + + The default cache topology model for an i386 PC machine is as follows: + ``l1d``, ``l1i``, and ``l2`` caches are per ``core``, while the ``l3`` + cache is per ``die``. + + Example: + + :: + + -machine smp-cache.0.cache=l1d,smp-cache.0.topology=core,smp-cache.1.cache=l1i,smp-cache.1.topology=core ERST DEF("M", HAS_ARG, QEMU_OPTION_M, diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f35d331..1ffea1a 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -4,9 +4,11 @@ #include "qapi/error.h" #include "qapi/qapi-visit-qom.h" #include "qobject/qobject.h" +#include "qobject/qbool.h" #include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qom/object_interfaces.h" @@ -177,9 +179,25 @@ char *object_property_help(const char *name, const char *type, g_string_append(str, description); } if (defval) { - g_autofree char *def_json = g_string_free(qobject_to_json(defval), - false); - g_string_append_printf(str, " (default: %s)", def_json); + g_autofree char *def_json = NULL; + const char *def; + + switch (qobject_type(defval)) { + case QTYPE_QSTRING: + def = qstring_get_str(qobject_to(QString, defval)); + break; + + case QTYPE_QBOOL: + def = qbool_get_bool(qobject_to(QBool, defval)) ? "on" : "off"; + break; + + default: + def_json = g_string_free(qobject_to_json(defval), false); + def = def_json; + break; + } + + g_string_append_printf(str, " (default: %s)", def); } return g_string_free(str, false); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 79e1427..2ebf0a1 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -55,6 +55,12 @@ dependencies = [ ] [[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] name = "pl011" version = "0.1.0" dependencies = [ @@ -100,6 +106,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "libc", "qemu_api_macros", "version_check", ] diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index fe73771..d0857b4 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,10 +2,10 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_void}, + ptr::{addr_of, addr_of_mut, NonNull}, }; use qemu_api::{ @@ -19,8 +19,8 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, - sysbus::{SysBusDevice, SysBusDeviceClass}, + qom::{ObjectImpl, Owned, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -50,11 +50,6 @@ impl std::ops::Index<hwaddr> for DeviceId { } } -impl DeviceId { - const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); - const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); -} - // FIFOs use 32-bit indices instead of usize, for compatibility with // the migration stream produced by the C version of this device. #[repr(transparent)] @@ -143,16 +138,24 @@ pub struct PL011Class { device_id: DeviceId, } +trait PL011Impl: SysBusDeviceImpl + IsA<PL011State> { + const DEVICE_ID: DeviceId; +} + +impl PL011Class { + fn class_init<T: PL011Impl>(&mut self) { + self.device_id = T::DEVICE_ID; + self.parent_class.class_init::<T>(); + } +} + unsafe impl ObjectType for PL011State { type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } -impl ClassInitImpl<PL011Class> for PL011State { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::ARM; - <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); - } +impl PL011Impl for PL011State { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); } impl ObjectImpl for PL011State { @@ -160,6 +163,7 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; } impl DeviceImpl for PL011State { @@ -176,6 +180,8 @@ impl ResettablePhasesImpl for PL011State { const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold); } +impl SysBusDeviceImpl for PL011State {} + impl PL011Registers { pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; @@ -726,13 +732,6 @@ pub struct PL011Luminary { parent_obj: ParentField<PL011State>, } -impl ClassInitImpl<PL011Class> for PL011Luminary { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::LUMINARY; - <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); - } -} - qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); unsafe impl ObjectType for PL011Luminary { @@ -742,7 +741,14 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; + + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; +} + +impl PL011Impl for PL011Luminary { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); } impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} +impl SysBusDeviceImpl for PL011Luminary {} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index dbef93f..0b2076d 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -2,8 +2,10 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::NonNull; -use std::os::raw::{c_int, c_void}; +use std::{ + os::raw::{c_int, c_void}, + ptr::NonNull, +}; use qemu_api::{ bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig index afd9803..42e4213 100644 --- a/rust/hw/timer/Kconfig +++ b/rust/hw/timer/Kconfig @@ -1,2 +1,3 @@ config X_HPET_RUST bool + default y if PC && HAVE_RUST diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 75ff5b3..be27eb0 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -23,7 +23,7 @@ use qemu_api::{ qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, - sysbus::SysBusDevice, + sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL}, }; @@ -836,6 +836,7 @@ impl ObjectImpl for HPETState { const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; } // TODO: Make these properties user-configurable! @@ -887,3 +888,5 @@ impl DeviceImpl for HPETState { impl ResettablePhasesImpl for HPETState { const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold); } + +impl SysBusDeviceImpl for HPETState {} diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index a51dd14..57747bc 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -16,6 +16,7 @@ rust-version = "1.63.0" [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } +libc = "0.2.162" [build-dependencies] version_check = "~0.9" diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2e9c107..bcf1cf7 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,6 +2,8 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() +libc_dep = dependency('libc-0.2-rs') + # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] @@ -22,6 +24,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/chardev.rs', 'src/c_str.rs', + 'src/errno.rs', 'src/irq.rs', 'src/memory.rs', 'src/module.rs', @@ -39,6 +42,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, + dependencies: libc_dep, ) rust.test('rust-qemu-api-tests', _qemu_api_rs, diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index fa1a18d..104dec3 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -92,3 +92,31 @@ macro_rules! assert_field_type { }; }; } + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs new file mode 100644 index 0000000..18d1014 --- /dev/null +++ b/rust/qemu-api/src/errno.rs @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{convert::TryFrom, io, io::ErrorKind}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From<Errno> for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From<io::ErrorKind> for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From<Errno> for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From<io::Error> for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result<Self::Out, Errno>; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From<u16> + std::ops::Neg<Output = Self::Out>; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result<Self::Out, Errno> { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use qemu_api::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use qemu_api::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result<u32> = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result<u8> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u8> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u16> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u16> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u32> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u32> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u64> = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result<u64> = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index d1c9dc9..34c1926 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -4,8 +4,7 @@ //! Bindings for interrupt sources -use core::ptr; -use std::{ffi::CStr, marker::PhantomData, os::raw::c_int}; +use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr}; use crate::{ bindings::{self, qemu_set_irq}, diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index ed1a8f9..05f38b5 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -19,6 +19,7 @@ pub mod c_str; pub mod callbacks; pub mod cell; pub mod chardev; +pub mod errno; pub mod irq; pub mod memory; pub mod module; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index fbf0ee2..634acf3 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -9,6 +9,8 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::errno; + pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 3a7aa4d..c136457 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -19,7 +19,7 @@ use crate::{ chardev::Chardev, irq::InterruptSource, prelude::*, - qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, + qom::{ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, }; @@ -86,7 +86,7 @@ unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>( } /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). @@ -113,7 +113,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { /// # Safety /// /// This function is only called through the QOM machinery and -/// used by the `ClassInitImpl<DeviceClass>` trait. +/// used by `DeviceClass::class_init`. /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. @@ -127,43 +127,41 @@ unsafe impl InterfaceType for ResettableClass { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; } -impl<T> ClassInitImpl<ResettableClass> for T -where - T: ResettablePhasesImpl, -{ - fn class_init(rc: &mut ResettableClass) { +impl ResettableClass { + /// Fill in the virtual methods of `ResettableClass` based on the + /// definitions in the `ResettablePhasesImpl` trait. + pub fn class_init<T: ResettablePhasesImpl>(&mut self) { if <T as ResettablePhasesImpl>::ENTER.is_some() { - rc.phases.enter = Some(rust_resettable_enter_fn::<T>); + self.phases.enter = Some(rust_resettable_enter_fn::<T>); } if <T as ResettablePhasesImpl>::HOLD.is_some() { - rc.phases.hold = Some(rust_resettable_hold_fn::<T>); + self.phases.hold = Some(rust_resettable_hold_fn::<T>); } if <T as ResettablePhasesImpl>::EXIT.is_some() { - rc.phases.exit = Some(rust_resettable_exit_fn::<T>); + self.phases.exit = Some(rust_resettable_exit_fn::<T>); } } } -impl<T> ClassInitImpl<DeviceClass> for T -where - T: ClassInitImpl<ObjectClass> + ClassInitImpl<ResettableClass> + DeviceImpl, -{ - fn class_init(dc: &mut DeviceClass) { +impl DeviceClass { + /// Fill in the virtual methods of `DeviceClass` based on the definitions in + /// the `DeviceImpl` trait. + pub fn class_init<T: DeviceImpl>(&mut self) { if <T as DeviceImpl>::REALIZE.is_some() { - dc.realize = Some(rust_realize_fn::<T>); + self.realize = Some(rust_realize_fn::<T>); } if let Some(vmsd) = <T as DeviceImpl>::vmsd() { - dc.vmsd = vmsd; + self.vmsd = vmsd; } let prop = <T as DeviceImpl>::properties(); if !prop.is_empty() { unsafe { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); } } - ResettableClass::interface_init::<T, DeviceState>(dc); - <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class); + ResettableClass::cast::<DeviceState>(self).class_init::<T>(); + self.parent_class.class_init::<T>(); } } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 3d5ab2d..5488643 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -37,11 +37,8 @@ //! * a trait for virtual method implementations, for example `DeviceImpl`. //! Child classes implement this trait to provide their own behavior for //! virtual methods. The trait's methods take `&self` to access instance data. -//! -//! * an implementation of [`ClassInitImpl`], for example -//! `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct; -//! the source for this is the `*Impl` trait; the associated consts and -//! functions if needed are wrapped to map C types into Rust types. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA<DeviceState>` for `DeviceImpl`. //! //! * a trait for instance methods, for example `DeviceMethods`. This trait is //! automatically implemented for any reference or smart pointer to a device @@ -52,6 +49,48 @@ //! This provides access to class-wide functionality that doesn't depend on //! instance data. Like instance methods, these are automatically inherited by //! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init<T: YourSubclassImpl>(&mut self) { +//! ... +//! klass.parent_class::class_init<T>(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::<Self>(self).class_init::<Self>(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::<Self>; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. use std::{ ffi::CStr, @@ -178,7 +217,7 @@ unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); } -unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( +unsafe extern "C" fn rust_class_init<T: ObjectType + ObjectImpl>( klass: *mut ObjectClass, _data: *mut c_void, ) { @@ -188,7 +227,7 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( // SAFETY: klass is a T::Class, since rust_class_init<T> // is called from QOM core as the class_init function // for class T - T::class_init(unsafe { klass.as_mut() }) + <T as ObjectImpl>::CLASS_INIT(unsafe { klass.as_mut() }) } unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) { @@ -277,19 +316,25 @@ pub unsafe trait InterfaceType: Sized { /// for this interface. const TYPE_NAME: &'static CStr; - /// Initialize the vtable for the interface; the generic argument `T` is the - /// type being initialized, while the generic argument `U` is the type that + /// Return the vtable for the interface; `U` is the type that /// lists the interface in its `TypeInfo`. /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init<T>` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::<DeviceState>(self).class_init::<T>(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// /// # Panics /// /// Panic if the incoming argument if `T` does not implement the interface. - fn interface_init< - T: ObjectType + ClassInitImpl<Self> + ClassInitImpl<U::Class>, - U: ObjectType, - >( - klass: &mut U::Class, - ) { + fn cast<U: ObjectType>(klass: &mut U::Class) -> &mut Self { unsafe { // SAFETY: upcasting to ObjectClass is always valid, and the // return type is either NULL or the argument itself @@ -298,8 +343,7 @@ pub unsafe trait InterfaceType: Sized { Self::TYPE_NAME.as_ptr(), ) .cast(); - - <T as ClassInitImpl<Self>>::class_init(result.as_mut().unwrap()) + result.as_mut().unwrap() } } } @@ -497,7 +541,7 @@ impl<T: ObjectType> ObjectDeref for &mut T {} impl<T: ObjectType> ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { +pub trait ObjectImpl: ObjectType + IsA<Object> { /// The parent of the type. This should match the first field of the /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; @@ -550,85 +594,26 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { // methods on ObjectClass const UNPARENT: Option<fn(&Self)> = None; -} -/// Internal trait used to automatically fill in a class struct. -/// -/// Each QOM class that has virtual methods describes them in a -/// _class struct_. Class structs include a parent field corresponding -/// to the vtable of the parent class, all the way up to [`ObjectClass`]. -/// Each QOM type has one such class struct; this trait takes care of -/// initializing the `T` part of the class struct, for the type that -/// implements the trait. -/// -/// Each struct will implement this trait with `T` equal to each -/// superclass. For example, a device should implement at least -/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and -/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are made -/// in one of two ways. -/// -/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` -/// crate itself. The Rust implementation of methods will come from a -/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl), -/// and `ClassInitImpl` is provided by blanket implementations that -/// operate on all implementors of the `*Impl`* trait. For example: -/// -/// ```ignore -/// impl<T> ClassInitImpl<DeviceClass> for T -/// where -/// T: ClassInitImpl<ObjectClass> + DeviceImpl, -/// ``` -/// -/// The bound on `ClassInitImpl<ObjectClass>` is needed so that, -/// after initializing the `DeviceClass` part of the class struct, -/// the parent [`ObjectClass`] is initialized as well. -/// -/// The other case is when manual implementation of the trait is needed. -/// This covers the following cases: -/// -/// * if a class implements a QOM interface, the Rust code _has_ to define its -/// own class struct `FooClass` and implement `ClassInitImpl<FooClass>`. -/// `ClassInitImpl<FooClass>`'s `class_init` method will then forward to -/// multiple other `class_init`s, for the interfaces as well as the -/// superclass. (Note that there is no Rust example yet for using interfaces). -/// -/// * for classes implemented outside the ``qemu-api`` crate, it's not possible -/// to add blanket implementations like the above one, due to orphan rules. In -/// that case, the easiest solution is to implement -/// `ClassInitImpl<YourSuperclass>` for each subclass and not have a -/// `YourSuperclassImpl` trait at all. -/// -/// ```ignore -/// impl ClassInitImpl<YourSuperclass> for YourSubclass { -/// fn class_init(klass: &mut YourSuperclass) { -/// klass.some_method = Some(Self::some_method); -/// <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); -/// } -/// } -/// ``` -/// -/// While this method incurs a small amount of code duplication, -/// it is generally limited to the recursive call on the last line. -/// This is because classes defined in Rust do not need the same -/// glue code that is needed when the classes are defined in C code. -/// You may consider using a macro if you have many subclasses. -pub trait ClassInitImpl<T> { - /// Initialize `klass` to point to the virtual method implementations + /// Store into the argument the virtual method implementations /// for `Self`. On entry, the virtual method pointers are set to /// the default values coming from the parent classes; the function /// can change them to override virtual methods of a parent class. /// - /// The virtual method implementations usually come from another - /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl) - /// when `T` is [`DeviceClass`](crate::qdev::DeviceClass). + /// Usually defined simply as `Self::Class::class_init::<Self>`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. /// - /// On entry, `klass`'s parent class is initialized, while the other fields + /// # Safety + /// + /// While `klass`'s parent class is initialized on entry, the other fields /// are all zero; it is therefore assumed that all fields in `T` can be /// zeroed, otherwise it would not be possible to provide the class as a /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable) /// to T; this is more easily done once Zeroable does not require a manual /// implementation (Rust 1.75.0). - fn class_init(klass: &mut T); + const CLASS_INIT: fn(&mut Self::Class); } /// # Safety @@ -641,13 +626,12 @@ unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) { T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } -impl<T> ClassInitImpl<ObjectClass> for T -where - T: ObjectImpl, -{ - fn class_init(oc: &mut ObjectClass) { +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init<T: ObjectImpl>(&mut self) { if <T as ObjectImpl>::UNPARENT.is_some() { - oc.unparent = Some(rust_unparent_fn::<T>); + self.unparent = Some(rust_unparent_fn::<T>); } } } diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index fa36e12..04821a2 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -14,8 +14,8 @@ use crate::{ irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, - qdev::{DeviceClass, DeviceState}, - qom::{ClassInitImpl, Owned}, + qdev::{DeviceImpl, DeviceState}, + qom::Owned, }; unsafe impl ObjectType for SysBusDevice { @@ -25,13 +25,14 @@ unsafe impl ObjectType for SysBusDevice { } qom_isa!(SysBusDevice: DeviceState, Object); -// TODO: add SysBusDeviceImpl -impl<T> ClassInitImpl<SysBusDeviceClass> for T -where - T: ClassInitImpl<DeviceClass>, -{ - fn class_init(sdc: &mut SysBusDeviceClass) { - <T as ClassInitImpl<DeviceClass>>::class_init(&mut sdc.parent_class); +// TODO: add virtual methods +pub trait SysBusDeviceImpl: DeviceImpl + IsA<SysBusDevice> {} + +impl SysBusDeviceClass { + /// Fill in the virtual methods of `SysBusDeviceClass` based on the + /// definitions in the `SysBusDeviceImpl` trait. + pub fn class_init<T: SysBusDeviceImpl>(self: &mut SysBusDeviceClass) { + self.parent_class.class_init::<T>(); } } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 92dbfb8..e398578 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -8,13 +8,14 @@ use std::{ }; use qemu_api::{ - bindings::*, + bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool}, c_str, cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, + qom::{ObjectImpl, ParentField}, + sysbus::SysBusDevice, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -40,6 +41,12 @@ pub struct DummyClass { parent_class: <DeviceState as ObjectType>::Class, } +impl DummyClass { + pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) { + self.parent_class.class_init::<T>(); + } +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -59,6 +66,7 @@ unsafe impl ObjectType for DummyState { impl ObjectImpl for DummyState { type ParentType = DeviceState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>; } impl ResettablePhasesImpl for DummyState {} @@ -72,14 +80,6 @@ impl DeviceImpl for DummyState { } } -// `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates -// orphan rule. -impl ClassInitImpl<DummyClass> for DummyState { - fn class_init(klass: &mut DummyClass) { - <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); - } -} - #[derive(qemu_api_macros::offsets)] #[repr(C)] #[derive(qemu_api_macros::Object)] @@ -101,20 +101,15 @@ unsafe impl ObjectType for DummyChildState { impl ObjectImpl for DummyChildState { type ParentType = DummyState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>; } impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} -impl ClassInitImpl<DummyClass> for DummyChildState { - fn class_init(klass: &mut DummyClass) { - <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); - } -} - -impl ClassInitImpl<DummyChildClass> for DummyChildState { - fn class_init(klass: &mut DummyChildClass) { - <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class); +impl DummyChildClass { + pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) { + self.parent_class.class_init::<T>(); } } diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 30677c3..e461c15 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -28,7 +28,7 @@ sub_file="${sub_tdir}/submodule.tar" # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/make-release b/scripts/make-release index 1b89b34..8c3594a 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,7 +41,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index a8066aa..3e8e008 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -168,6 +168,7 @@ meson_options_help() { printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' + printf "%s\n" ' pvg macOS paravirtualized graphics support' printf "%s\n" ' qatzip QATzip compression support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' @@ -436,6 +437,8 @@ _meson_option_parse() { --enable-png) printf "%s" -Dpng=enabled ;; --disable-png) printf "%s" -Dpng=disabled ;; --prefix=*) quote_sh "-Dprefix=$2" ;; + --enable-pvg) printf "%s" -Dpvg=enabled ;; + --disable-pvg) printf "%s" -Dpvg=disabled ;; --enable-qatzip) printf "%s" -Dqatzip=enabled ;; --disable-qatzip) printf "%s" -Dqatzip=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; diff --git a/stubs/meson.build b/stubs/meson.build index b0fee37..63392f5 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -62,7 +62,7 @@ if have_user stub_ss.add(files('qdev.c')) endif - stub_ss.add(files('monitor-fd.c')) + stub_ss.add(files('monitor-internal.c')) endif if have_system diff --git a/stubs/monitor-fd.c b/stubs/monitor-fd.c deleted file mode 100644 index 9bb6749..0000000 --- a/stubs/monitor-fd.c +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "qemu/osdep.h" -#include "monitor/monitor.h" - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - abort(); -} diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 50f173f..d12d346 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /bilge-impl-0.2.0 /either-1.12.0 /itertools-0.11.0 +/libc-0.2.162 /proc-macro-error-1.0.4 /proc-macro-error-attr-1.0.4 /proc-macro2-1.0.84 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap new file mode 100644 index 0000000..bbe08f8 --- /dev/null +++ b/subprojects/libc-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libc-0.2.162 +source_url = https://crates.io/api/v1/crates/libc/0.2.162/download +source_filename = libc-0.2.162.tar.gz +source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 +#method = cargo +patch_directory = libc-0.2-rs diff --git a/subprojects/packagefiles/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build new file mode 100644 index 0000000..ac4f80d --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson.build @@ -0,0 +1,37 @@ +project('libc-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.162', + license: 'MIT OR Apache-2.0', + default_options: []) + +_libc_rs = static_library( + 'libc', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2015', 'build.rust_std=2015'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', + ], + dependencies: [], +) + +libc_dep = declare_dependency( + link_with: _libc_rs, +) + +meson.override_dependency('libc-0.2-rs', libc_dep) diff --git a/system/physmem.c b/system/physmem.c index 67bdf63..eff8b55 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1882,7 +1882,11 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) if (new_block->flags & RAM_GUEST_MEMFD) { int ret; - assert(kvm_enabled()); + if (!kvm_enabled()) { + error_setg(errp, "cannot set up private guest memory for %s: KVM required", + object_get_typename(OBJECT(current_machine->cgs))); + goto out_free; + } assert(new_block->guest_memfd < 0); ret = ram_block_discard_require(true); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 72ab147..0cd9b70 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -247,6 +247,9 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, case CPU_TOPOLOGY_LEVEL_CORE: num_ids = 1 << apicid_core_offset(topo_info); break; + case CPU_TOPOLOGY_LEVEL_MODULE: + num_ids = 1 << apicid_module_offset(topo_info); + break; case CPU_TOPOLOGY_LEVEL_DIE: num_ids = 1 << apicid_die_offset(topo_info); break; @@ -255,7 +258,7 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, break; default: /* - * Currently there is no use case for THREAD and MODULE, so use + * Currently there is no use case for THREAD, so use * assert directly to facilitate debugging. */ g_assert_not_reached(); @@ -5495,6 +5498,130 @@ static const X86CPUDefinition builtin_x86_defs[] = { .model_id = "AMD EPYC-Genoa Processor", .cache_info = &epyc_genoa_cache_info, }, + { + .name = "YongFeng", + .level = 0x1F, + .vendor = CPUID_VENDOR_ZHAOXIN1, + .family = 7, + .model = 11, + .stepping = 3, + /* missing: CPUID_HT, CPUID_TM, CPUID_PBE */ + .features[FEAT_1_EDX] = + CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_ACPI | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | + CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | + CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | + CPUID_PSE | CPUID_DE | CPUID_VME | CPUID_FP87, + /* + * missing: CPUID_EXT_OSXSAVE, CPUID_EXT_XTPR, CPUID_EXT_TM2, + * CPUID_EXT_EST, CPUID_EXT_SMX, CPUID_EXT_VMX + */ + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_TSC_DEADLINE_TIMER | + CPUID_EXT_POPCNT | CPUID_EXT_MOVBE | CPUID_EXT_X2APIC | + CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_PCID | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_BMI2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_FSGSBASE, + /* missing: CPUID_7_0_ECX_OSPKE */ + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_UMIP, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, + .features[FEAT_8000_0007_EDX] = CPUID_APM_INVTSC, + /* + * TODO: When the Linux kernel introduces other existing definitions + * for this leaf, remember to update the definitions here. + */ + .features[FEAT_C000_0001_EDX] = + CPUID_C000_0001_EDX_PMM_EN | CPUID_C000_0001_EDX_PMM | + CPUID_C000_0001_EDX_PHE_EN | CPUID_C000_0001_EDX_PHE | + CPUID_C000_0001_EDX_ACE2 | + CPUID_C000_0001_EDX_XCRYPT_EN | CPUID_C000_0001_EDX_XCRYPT | + CPUID_C000_0001_EDX_XSTORE_EN | CPUID_C000_0001_EDX_XSTORE, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | + MSR_ARCH_CAP_MDS_NO | MSR_ARCH_CAP_PSCHANGE_MC_NO | + MSR_ARCH_CAP_SSB_NO, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_INVLPG_EXITING | + VMX_CPU_BASED_MWAIT_EXITING | VMX_CPU_BASED_RDPMC_EXITING | + VMX_CPU_BASED_RDTSC_EXITING | VMX_CPU_BASED_CR3_LOAD_EXITING | + VMX_CPU_BASED_CR3_STORE_EXITING | VMX_CPU_BASED_CR8_LOAD_EXITING | + VMX_CPU_BASED_CR8_STORE_EXITING | VMX_CPU_BASED_TPR_SHADOW | + VMX_CPU_BASED_VIRTUAL_NMI_PENDING | VMX_CPU_BASED_MOV_DR_EXITING | + VMX_CPU_BASED_UNCOND_IO_EXITING | VMX_CPU_BASED_USE_IO_BITMAPS | + VMX_CPU_BASED_MONITOR_TRAP_FLAG | VMX_CPU_BASED_USE_MSR_BITMAPS | + VMX_CPU_BASED_MONITOR_EXITING | VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + /* + * missing: VMX_SECONDARY_EXEC_PAUSE_LOOP_EXITING, + * VMX_SECONDARY_EXEC_TSC_SCALING + */ + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | + VMX_SECONDARY_EXEC_RDTSCP | VMX_SECONDARY_EXEC_ENABLE_VPID | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_WBINVD_EXITING | + VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | + VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | + VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_ENABLE_PML, + .features[FEAT_VMX_PINBASED_CTLS] = + VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | + VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | + VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | VMX_VM_EXIT_HOST_ADDR_SPACE_SIZE | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + /* missing: VMX_VM_ENTRY_SMM, VMX_VM_ENTRY_DEACT_DUAL_MONITOR */ + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, + /* + * missing: MSR_VMX_MISC_ACTIVITY_SHUTDOWN, + * MSR_VMX_MISC_ACTIVITY_WAIT_SIPI + */ + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + /* missing: MSR_VMX_EPT_UC */ + .features[FEAT_VMX_EPT_VPID_CAPS] = + MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | MSR_VMX_EPT_INVVPID | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Zhaoxin YongFeng Processor", + }, }; /* @@ -7677,9 +7804,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* * The Linux kernel checks for the CMPLegacy bit and * discards multiple thread information if it is set. - * So don't set it here for Intel to make Linux guests happy. + * So don't set it here for Intel (and other processors + * following Intel's behavior) to make Linux guests happy. */ - if (!IS_INTEL_CPU(env)) { + if (!IS_INTEL_CPU(env) && !IS_ZHAOXIN_CPU(env)) { env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; } } @@ -7903,6 +8031,64 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu) cpu->hyperv_limits[2] = 0; } +#ifndef CONFIG_USER_ONLY +static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, + Error **errp) +{ + CPUX86State *env = &cpu->env; + CpuTopologyLevel level; + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1d_cache->share_level = level; + env->cache_info_amd.l1d_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_cpuid4.l1d_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_amd.l1d_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1i_cache->share_level = level; + env->cache_info_amd.l1i_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_cpuid4.l1i_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_amd.l1i_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l2_cache->share_level = level; + env->cache_info_amd.l2_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_cpuid4.l2_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_amd.l2_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l3_cache->share_level = level; + env->cache_info_amd.l3_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_cpuid4.l3_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_amd.l3_cache->share_level); + } + + if (!machine_check_smp_cache(ms, errp)) { + return false; + } + return true; +} +#endif + static void x86_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -8142,6 +8328,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->smp_props.has_caches) { + if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { + return; + } + } + qemu_register_reset(x86_cpu_machine_reset_cb, cpu); if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c67b42d..10ce019 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1110,6 +1110,27 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* CPUID[0x80000007].EDX flags: */ #define CPUID_APM_INVTSC (1U << 8) +/* "rng" RNG present (xstore) */ +#define CPUID_C000_0001_EDX_XSTORE (1U << 2) +/* "rng_en" RNG enabled */ +#define CPUID_C000_0001_EDX_XSTORE_EN (1U << 3) +/* "ace" on-CPU crypto (xcrypt) */ +#define CPUID_C000_0001_EDX_XCRYPT (1U << 6) +/* "ace_en" on-CPU crypto enabled */ +#define CPUID_C000_0001_EDX_XCRYPT_EN (1U << 7) +/* Advanced Cryptography Engine v2 */ +#define CPUID_C000_0001_EDX_ACE2 (1U << 8) +/* ACE v2 enabled */ +#define CPUID_C000_0001_EDX_ACE2_EN (1U << 9) +/* PadLock Hash Engine */ +#define CPUID_C000_0001_EDX_PHE (1U << 10) +/* PHE enabled */ +#define CPUID_C000_0001_EDX_PHE_EN (1U << 11) +/* PadLock Montgomery Multiplier */ +#define CPUID_C000_0001_EDX_PMM (1U << 12) +/* PMM enabled */ +#define CPUID_C000_0001_EDX_PMM_EN (1U << 13) + #define CPUID_VENDOR_SZ 12 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ @@ -1122,7 +1143,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ #define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN1_1 0x746E6543 /* "Cent" */ +#define CPUID_VENDOR_ZHAOXIN1_2 0x48727561 /* "aurH" */ +#define CPUID_VENDOR_ZHAOXIN1_3 0x736C7561 /* "auls" */ + +#define CPUID_VENDOR_ZHAOXIN2_1 0x68532020 /* " Sh" */ +#define CPUID_VENDOR_ZHAOXIN2_2 0x68676E61 /* "angh" */ +#define CPUID_VENDOR_ZHAOXIN2_3 0x20206961 /* "ai " */ + +#define CPUID_VENDOR_ZHAOXIN1 "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN2 " Shanghai " #define CPUID_VENDOR_HYGON "HygonGenuine" @@ -1132,6 +1162,15 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3) +#define IS_ZHAOXIN1_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN1_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN1_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN1_3) +#define IS_ZHAOXIN2_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN2_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN2_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN2_3) +#define IS_ZHAOXIN_CPU(env) (IS_ZHAOXIN1_CPU(env) || IS_ZHAOXIN2_CPU(env)) #define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index e99c02c..044ad23 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -18,7 +18,9 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); -void hvf_handle_io(CPUArchState *, uint16_t, void *, int, int, int); +void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); +void hvf_simulate_rdmsr(CPUX86State *env); +void hvf_simulate_wrmsr(CPUX86State *env); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index ca08f07..9ba0e04 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -61,6 +61,7 @@ #include "vmx.h" #include "x86.h" #include "x86_descr.h" +#include "x86_flags.h" #include "x86_mmu.h" #include "x86_decode.h" #include "x86_emu.h" @@ -103,7 +104,7 @@ static void update_apic_tpr(CPUState *cpu) #define VECTORING_INFO_VECTOR_MASK 0xff -void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer, +void hvf_handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { int i; @@ -434,6 +435,264 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +void hvf_load_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); + RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); + RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); + RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); + RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); + RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); + RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); + RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); + for (i = 8; i < 16; i++) { + RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); + } + + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); + rflags_to_lflags(env); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); +} + +void hvf_store_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); + wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); + wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); + wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); + wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); + wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); + wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); + wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); + for (i = 8; i < 16; i++) { + wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); + } + + lflags_to_rflags(env); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); +} + +void hvf_simulate_rdmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t val = 0; + + switch (msr) { + case MSR_IA32_TSC: + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); + break; + case MSR_IA32_APICBASE: + val = cpu_get_apic_base(cpu->apic_state); + break; + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_read(index, &val); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_IA32_UCODE_REV: + val = cpu->ucode_rev; + break; + case MSR_EFER: + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); + break; + case MSR_FSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); + break; + case MSR_GSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); + break; + case MSR_KERNELGSBASE: + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_IA32_MISC_ENABLE: + val = env->msr_ia32_misc_enable; + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; + break; + case MSR_MTRRfix64K_00000: + val = env->mtrr_fixed[0]; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; + break; + case MSR_MTRRdefType: + val = env->mtrr_deftype; + break; + case MSR_CORE_THREAD_COUNT: + val = cpu_x86_get_msr_core_thread_count(cpu); + break; + default: + /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ + val = 0; + break; + } + + RAX(env) = (uint32_t)val; + RDX(env) = (uint32_t)(val >> 32); +} + +void hvf_simulate_wrmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); + + switch (msr) { + case MSR_IA32_TSC: + break; + case MSR_IA32_APICBASE: { + int r; + + r = cpu_set_apic_base(cpu->apic_state, data); + if (r < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_write(index, data); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_FSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); + break; + case MSR_GSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); + break; + case MSR_KERNELGSBASE: + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_EFER: + /*printf("new efer %llx\n", EFER(cs));*/ + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); + if (data & MSR_EFER_NXE) { + hv_vcpu_invalidate_tlb(cs->accel->fd); + } + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; + break; + case MSR_MTRRfix64K_00000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; + break; + case MSR_MTRRdefType: + env->mtrr_deftype = data; + break; + default: + break; + } + + /* Related to support known hypervisor interface */ + /* if (g_hypervisor_iface) + g_hypervisor_iface->wrmsr_handler(cs, msr, data); + + printf("write msr %llx\n", RCX(cs));*/ +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -517,10 +776,10 @@ int hvf_vcpu_exec(CPUState *cpu) if (ept_emulation_fault(slot, gpa, exit_qual)) { struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } break; @@ -535,8 +794,8 @@ int hvf_vcpu_exec(CPUState *cpu) if (!string && in) { uint64_t val = 0; - load_regs(cpu); - hvf_handle_io(env, port, &val, 0, size, 1); + hvf_load_regs(cpu); + hvf_handle_io(env_cpu(env), port, &val, 0, size, 1); if (size == 1) { AL(env) = val; } else if (size == 2) { @@ -547,21 +806,21 @@ int hvf_vcpu_exec(CPUState *cpu) RAX(env) = (uint64_t)val; } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } else if (!string && !in) { RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); - hvf_handle_io(env, port, &RAX(env), 1, size, 1); + hvf_handle_io(env_cpu(env), port, &RAX(env), 1, size, 1); macvm_set_rip(cpu, rip + ins_len); break; } struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); assert(ins_len == decode.len); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } @@ -614,21 +873,21 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_RDMSR: case EXIT_REASON_WRMSR: { - load_regs(cpu); + hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); } else { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_CR_ACCESS: { int cr; int reg; - load_regs(cpu); + hvf_load_regs(cpu); cr = exit_qual & 15; reg = (exit_qual >> 8) & 15; @@ -656,16 +915,16 @@ int hvf_vcpu_exec(CPUState *cpu) abort(); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_APIC_ACCESS: { /* TODO */ struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_TPR: { @@ -674,7 +933,7 @@ int hvf_vcpu_exec(CPUState *cpu) } case EXIT_REASON_TASK_SWITCH: { uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); - x68_segment_selector sel = {.sel = exit_qual & 0xffff}; + x86_segment_selector sel = {.sel = exit_qual & 0xffff}; vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3, vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo & VMCS_INTR_T_MASK); diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 80e3613..a0ede13 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -48,7 +48,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; @@ -78,7 +78,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 3570f29..063cd0b 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -183,7 +183,7 @@ static inline uint32_t x86_call_gate_offset(x86_call_gate *gate) #define GDT_SEL 0 #define LDT_SEL 1 -typedef struct x68_segment_selector { +typedef struct x86_segment_selector { union { uint16_t sel; struct { @@ -192,7 +192,7 @@ typedef struct x68_segment_selector { uint16_t index:13; }; }; -} __attribute__ ((__packed__)) x68_segment_selector; +} __attribute__ ((__packed__)) x86_segment_selector; /* useful register access macros */ #define x86_reg(cpu, reg) ((x86_register *) &cpu->regs[reg]) @@ -250,10 +250,10 @@ typedef struct x68_segment_selector { /* deal with GDT/LDT descriptors in memory */ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, int gate); diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index a4a28f1..d6d5894 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -1893,6 +1893,16 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) } } +static struct x86_segment_descriptor get_cs_descriptor(CPUState *s) +{ + struct vmx_segment vmx_cs; + x86_segment_descriptor cs; + vmx_read_segment_descriptor(s, &vmx_cs, R_CS); + vmx_segment_to_x86_descriptor(s, &vmx_cs, &cs); + + return cs; +} + void set_addressing_size(CPUX86State *env, struct x86_decode *decode) { decode->addressing_size = -1; @@ -1904,10 +1914,9 @@ void set_addressing_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->addr_size_override) { decode->addressing_size = 2; } else { @@ -1941,10 +1950,9 @@ void set_operand_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->op_size_override) { decode->operand_size = 2; } else{ diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index f33836d..7b599c9 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -60,14 +60,14 @@ uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg) return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); } -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) { - x68_segment_selector sel; + x86_segment_selector sel; sel.sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector); return sel; } -void vmx_write_segment_selector(CPUState *cpu, x68_segment_selector selector, X86Seg seg) +void vmx_write_segment_selector(CPUState *cpu, x86_segment_selector selector, X86Seg seg) { wvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector, selector.sel); } @@ -90,7 +90,7 @@ void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Se wvmcs(cpu->accel->fd, sf->ar_bytes, desc->ar); } -void x86_segment_descriptor_to_vmx(CPUState *cpu, x68_segment_selector selector, +void x86_segment_descriptor_to_vmx(CPUState *cpu, x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc) { diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h index 9f06014..ce5de98 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -34,10 +34,10 @@ void vmx_read_segment_descriptor(CPUState *cpu, void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, enum X86Seg seg); -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, enum X86Seg seg); void vmx_write_segment_selector(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, enum X86Seg seg); uint64_t vmx_read_segment_base(CPUState *cpu, enum X86Seg seg); @@ -45,7 +45,7 @@ void vmx_write_segment_base(CPUState *cpu, enum X86Seg seg, uint64_t base); void x86_segment_descriptor_to_vmx(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 69c61c9..ebba80a 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -44,9 +44,7 @@ #include "x86_flags.h" #include "vmcs.h" #include "vmx.h" - -void hvf_handle_io(CPUState *cs, uint16_t port, void *data, - int direction, int size, uint32_t count); +#include "hvf-i386.h" #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ { \ @@ -663,8 +661,7 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void raise_exception(CPUX86State *env, int exception_index, - int error_code) +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code) { env->exception_nr = exception_index; env->error_code = error_code; @@ -672,227 +669,15 @@ static void raise_exception(CPUX86State *env, int exception_index, env->exception_injected = 1; } -void simulate_rdmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t val = 0; - - switch (msr) { - case MSR_IA32_TSC: - val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); - break; - case MSR_IA32_APICBASE: - val = cpu_get_apic_base(cpu->apic_state); - break; - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_read(index, &val); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_IA32_UCODE_REV: - val = cpu->ucode_rev; - break; - case MSR_EFER: - val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); - break; - case MSR_FSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); - break; - case MSR_GSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); - break; - case MSR_KERNELGSBASE: - val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_IA32_MISC_ENABLE: - val = env->msr_ia32_misc_enable; - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; - break; - case MSR_MTRRfix64K_00000: - val = env->mtrr_fixed[0]; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; - break; - case MSR_MTRRdefType: - val = env->mtrr_deftype; - break; - case MSR_CORE_THREAD_COUNT: - val = cpu_x86_get_msr_core_thread_count(cpu); - break; - default: - /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ - val = 0; - break; - } - - RAX(env) = (uint32_t)val; - RDX(env) = (uint32_t)(val >> 32); -} - static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); env->eip += decode->len; } -void simulate_wrmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); - - switch (msr) { - case MSR_IA32_TSC: - break; - case MSR_IA32_APICBASE: { - int r; - - r = cpu_set_apic_base(cpu->apic_state, data); - if (r < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_write(index, data); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_FSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); - break; - case MSR_GSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); - break; - case MSR_KERNELGSBASE: - wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_EFER: - /*printf("new efer %llx\n", EFER(cs));*/ - wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); - if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cs->accel->fd); - } - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; - break; - case MSR_MTRRfix64K_00000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; - break; - case MSR_MTRRdefType: - env->mtrr_deftype = data; - break; - default: - break; - } - - /* Related to support known hypervisor interface */ - /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cs, msr, data); - - printf("write msr %llx\n", RCX(cs));*/ -} - static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); env->eip += decode->len; } @@ -1454,58 +1239,8 @@ static void init_cmd_handler(void) } } -void load_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); - for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); - } - - env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); - rflags_to_lflags(env); - env->eip = rreg(cs->accel->fd, HV_X86_RIP); -} - -void store_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); - wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); - wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); - wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); - wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); - wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); - wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); - wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); - for (i = 8; i < 16; i++) { - wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); - } - - lflags_to_rflags(env); - wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cs, env->eip); -} - bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { - /*if (hvf_vcpu_id(cs)) - printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cs), env->eip, - decode_cmd_to_string(ins->cmd));*/ - if (!_cmd_handler[ins->cmd].handler) { printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip, ins->cmd, ins->opcode[0], diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 8bd9760..bc0fc72 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -25,12 +25,7 @@ void init_emu(void); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); - -void load_regs(CPUState *cpu); -void store_regs(CPUState *cpu); - -void simulate_rdmsr(CPUX86State *env); -void simulate_wrmsr(CPUX86State *env); +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code); target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index bcd844c..1612179 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -76,16 +76,16 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) RSI(env) = tss->esi; RDI(env) = tss->edi; - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ldt}}, R_LDTR); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->es}}, R_ES); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->cs}}, R_CS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ss}}, R_SS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ds}}, R_DS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->fs}}, R_FS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->gs}}, R_GS); } -static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, +static int task_switch_32(CPUState *cpu, x86_segment_selector tss_sel, x86_segment_selector old_tss_sel, uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) { struct x86_tss_segment32 tss_seg; @@ -108,7 +108,7 @@ static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segme return 0; } -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) { uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP); if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && @@ -119,10 +119,10 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea return; } - load_regs(cpu); + hvf_load_regs(cpu); struct x86_segment_descriptor curr_tss_desc, next_tss_desc; - x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); + x86_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); uint32_t desc_limit; struct x86_call_gate task_gate_desc; @@ -140,7 +140,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_read_call_gate(cpu, &task_gate_desc, gate); dpl = task_gate_desc.dpl; - x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); + x86_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); if (tss_sel.rpl > dpl || cs.rpl > dpl) ;//DPRINTF("emulate_gp"); } @@ -178,7 +178,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); - store_regs(cpu); + hvf_store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->accel->fd); } diff --git a/target/i386/hvf/x86_task.h b/target/i386/hvf/x86_task.h index 4eaa61a..b9afac6 100644 --- a/target/i386/hvf/x86_task.h +++ b/target/i386/hvf/x86_task.h @@ -15,6 +15,6 @@ #ifndef HVF_X86_TASK_H #define HVF_X86_TASK_H -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type); #endif diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 423a89b..8c46ce8 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -31,4 +31,7 @@ void hvf_get_xsave(CPUState *cs); void hvf_get_msrs(CPUState *cs); void vmx_clear_int_window_exiting(CPUState *cs); void vmx_update_tpr(CPUState *cs); + +void hvf_load_regs(CPUState *cpu); +void hvf_store_regs(CPUState *cpu); #endif diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index cca24b9..d7ecf72 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -700,13 +700,6 @@ static void rv128_base_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); CPURISCVState *env = &cpu->env; - if (qemu_tcg_mttcg_enabled()) { - /* Missing 128-bit aligned atomics */ - error_report("128-bit RISC-V currently does not work with Multi " - "Threaded TCG. Please use: -accel tcg,thread=single"); - exit(EXIT_FAILURE); - } - cpu->cfg.mmu = true; cpu->cfg.pmp = true; @@ -3051,15 +3044,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_CPU(type_name, misa_mxl_max, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_CPU, \ - .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ - } - #define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \ { \ .name = (type_name), \ diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 0a13728..d7e694f 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1014,6 +1014,7 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) { RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); if (!riscv_cpu_tcg_compatible(cpu)) { g_autofree char *name = riscv_cpu_get_name(cpu); @@ -1022,6 +1023,14 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) return false; } + if (mcc->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { + /* Missing 128-bit aligned atomics */ + error_setg(errp, + "128-bit RISC-V currently does not work with Multi " + "Threaded TCG. Please use: -accel tcg,thread=single"); + return false; + } + #ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; |