aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-03-03 10:20:59 +0800
committerStefan Hajnoczi <stefanha@redhat.com>2025-03-03 10:20:59 +0800
commit8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0 (patch)
tree6059933b4c0205cdfb5f597f94a2ad2d0b4debfd
parent70fc2bde913ef8a6b77ded6831534d9c79541e17 (diff)
parenta4e749780bd20593c0c386612a51bf4d64a80132 (diff)
downloadqemu-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>
-rw-r--r--Kconfig.host3
-rw-r--r--docs/devel/rust.rst19
-rw-r--r--hw/core/machine-smp.c2
-rw-r--r--hw/display/Kconfig4
-rw-r--r--hw/display/meson.build9
-rw-r--r--hw/i386/pc.c4
-rw-r--r--hw/timer/hpet.c38
-rw-r--r--include/hw/boards.h3
-rw-r--r--meson.build14
-rw-r--r--meson_options.txt2
-rw-r--r--qemu-options.hx30
-rw-r--r--qom/object_interfaces.c24
-rw-r--r--rust/Cargo.lock7
-rw-r--r--rust/hw/char/pl011/src/device.rs46
-rw-r--r--rust/hw/char/pl011/src/device_class.rs6
-rw-r--r--rust/hw/timer/Kconfig1
-rw-r--r--rust/hw/timer/hpet/src/hpet.rs5
-rw-r--r--rust/qemu-api/Cargo.toml1
-rw-r--r--rust/qemu-api/meson.build4
-rw-r--r--rust/qemu-api/src/assertions.rs28
-rw-r--r--rust/qemu-api/src/errno.rs345
-rw-r--r--rust/qemu-api/src/irq.rs3
-rw-r--r--rust/qemu-api/src/lib.rs1
-rw-r--r--rust/qemu-api/src/prelude.rs2
-rw-r--r--rust/qemu-api/src/qdev.rs40
-rw-r--r--rust/qemu-api/src/qom.rs168
-rw-r--r--rust/qemu-api/src/sysbus.rs19
-rw-r--r--rust/qemu-api/tests/tests.rs35
-rwxr-xr-xscripts/archive-source.sh2
-rwxr-xr-xscripts/make-release2
-rw-r--r--scripts/meson-buildoptions.sh3
-rw-r--r--stubs/meson.build2
-rw-r--r--stubs/monitor-fd.c9
-rw-r--r--subprojects/.gitignore1
-rw-r--r--subprojects/libc-0.2-rs.wrap7
-rw-r--r--subprojects/packagefiles/libc-0.2-rs/meson.build37
-rw-r--r--system/physmem.c6
-rw-r--r--target/i386/cpu.c200
-rw-r--r--target/i386/cpu.h41
-rw-r--r--target/i386/hvf/hvf-i386.h4
-rw-r--r--target/i386/hvf/hvf.c295
-rw-r--r--target/i386/hvf/x86.c4
-rw-r--r--target/i386/hvf/x86.h8
-rw-r--r--target/i386/hvf/x86_decode.c20
-rw-r--r--target/i386/hvf/x86_descr.c8
-rw-r--r--target/i386/hvf/x86_descr.h6
-rw-r--r--target/i386/hvf/x86_emu.c273
-rw-r--r--target/i386/hvf/x86_emu.h7
-rw-r--r--target/i386/hvf/x86_task.c26
-rw-r--r--target/i386/hvf/x86_task.h2
-rw-r--r--target/i386/hvf/x86hvf.h3
-rw-r--r--target/riscv/cpu.c16
-rw-r--r--target/riscv/tcg/tcg-cpu.c9
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;