mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-09-09 00:07:57 -06:00
* 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----- 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>
This commit is contained in:
commit
8d56d0fd2f
53 changed files with 1275 additions and 579 deletions
|
@ -61,3 +61,6 @@ config HV_BALLOON_POSSIBLE
|
||||||
|
|
||||||
config HAVE_RUST
|
config HAVE_RUST
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config MAC_PVG
|
||||||
|
bool
|
||||||
|
|
|
@ -139,16 +139,22 @@ anymore.
|
||||||
Writing Rust code in QEMU
|
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`` for bindings to C code and useful functionality
|
||||||
|
|
||||||
* ``qemu_api_macros`` defines several procedural macros that are useful when
|
* ``qemu_api_macros`` defines several procedural macros that are useful when
|
||||||
writing C code
|
writing C code
|
||||||
|
|
||||||
* ``pl011`` (under ``rust/hw/char/pl011``) is the sample device that is being
|
* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``)
|
||||||
used to further develop ``qemu_api`` and ``qemu_api_macros``. It is a functional
|
are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are
|
||||||
replacement for the ``hw/char/pl011.c`` file.
|
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.
|
This section explains how to work with them.
|
||||||
|
|
||||||
|
@ -179,6 +185,7 @@ module status
|
||||||
``callbacks`` complete
|
``callbacks`` complete
|
||||||
``cell`` stable
|
``cell`` stable
|
||||||
``c_str`` complete
|
``c_str`` complete
|
||||||
|
``errno`` complete
|
||||||
``irq`` complete
|
``irq`` complete
|
||||||
``memory`` stable
|
``memory`` stable
|
||||||
``module`` complete
|
``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
|
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
|
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"
|
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``.
|
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
|
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
|
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``
|
the procedural macro, and the second returning ``proc_macro::TokenStream``
|
||||||
which is the actual procedural macro. The former's name is the same as
|
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
|
the latter with the ``_or_error`` suffix. The code for the latter is more
|
||||||
|
|
|
@ -332,6 +332,8 @@ bool machine_parse_smp_cache(MachineState *ms,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mc->smp_props.has_caches = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,10 +141,6 @@ config XLNX_DISPLAYPORT
|
||||||
config DM163
|
config DM163
|
||||||
bool
|
bool
|
||||||
|
|
||||||
config MAC_PVG
|
|
||||||
bool
|
|
||||||
default y
|
|
||||||
|
|
||||||
config MAC_PVG_MMIO
|
config MAC_PVG_MMIO
|
||||||
bool
|
bool
|
||||||
depends on MAC_PVG && AARCH64
|
depends on MAC_PVG && AARCH64
|
||||||
|
|
|
@ -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])
|
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: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('apple-gfx.m', 'apple-gfx-pci.m')])
|
||||||
system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal])
|
system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m')])
|
||||||
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
|
|
||||||
|
|
||||||
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
|
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
|
||||||
virtio_gpu_ss = ss.source_set()
|
virtio_gpu_ss = ss.source_set()
|
||||||
|
|
|
@ -1798,6 +1798,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||||
mc->nvdimm_supported = true;
|
mc->nvdimm_supported = true;
|
||||||
mc->smp_props.dies_supported = true;
|
mc->smp_props.dies_supported = true;
|
||||||
mc->smp_props.modules_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";
|
mc->default_ram_id = "pc.ram";
|
||||||
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;
|
pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ struct HPETState {
|
||||||
uint8_t rtc_irq_level;
|
uint8_t rtc_irq_level;
|
||||||
qemu_irq pit_enabled;
|
qemu_irq pit_enabled;
|
||||||
uint8_t num_timers;
|
uint8_t num_timers;
|
||||||
|
uint8_t num_timers_save;
|
||||||
uint32_t intcap;
|
uint32_t intcap;
|
||||||
HPETTimer timer[HPET_MAX_TIMERS];
|
HPETTimer timer[HPET_MAX_TIMERS];
|
||||||
|
|
||||||
|
@ -237,15 +238,12 @@ static int hpet_pre_save(void *opaque)
|
||||||
s->hpet_counter = hpet_get_ticks(s);
|
s->hpet_counter = hpet_get_ticks(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
/*
|
||||||
}
|
* 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
|
||||||
static int hpet_pre_load(void *opaque)
|
* that was configured.
|
||||||
{
|
*/
|
||||||
HPETState *s = opaque;
|
s->num_timers_save = s->num_timers;
|
||||||
|
|
||||||
/* version 1 only supports 3, later versions will load the actual value */
|
|
||||||
s->num_timers = HPET_MIN_TIMERS;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,12 +251,7 @@ static bool hpet_validate_num_timers(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
HPETState *s = opaque;
|
HPETState *s = opaque;
|
||||||
|
|
||||||
if (s->num_timers < HPET_MIN_TIMERS) {
|
return s->num_timers == s->num_timers_save;
|
||||||
return false;
|
|
||||||
} else if (s->num_timers > HPET_MAX_TIMERS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hpet_post_load(void *opaque, int version_id)
|
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);
|
- 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,14 +330,13 @@ static const VMStateDescription vmstate_hpet = {
|
||||||
.version_id = 2,
|
.version_id = 2,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.pre_save = hpet_pre_save,
|
.pre_save = hpet_pre_save,
|
||||||
.pre_load = hpet_pre_load,
|
|
||||||
.post_load = hpet_post_load,
|
.post_load = hpet_post_load,
|
||||||
.fields = (const VMStateField[]) {
|
.fields = (const VMStateField[]) {
|
||||||
VMSTATE_UINT64(config, HPETState),
|
VMSTATE_UINT64(config, HPETState),
|
||||||
VMSTATE_UINT64(isr, HPETState),
|
VMSTATE_UINT64(isr, HPETState),
|
||||||
VMSTATE_UINT64(hpet_counter, HPETState),
|
VMSTATE_UINT64(hpet_counter, HPETState),
|
||||||
VMSTATE_UINT8_V(num_timers, HPETState, 2),
|
VMSTATE_UINT8_V(num_timers_save, HPETState, 2),
|
||||||
VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers),
|
VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers),
|
||||||
VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
|
VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
|
||||||
vmstate_hpet_timer, HPETTimer),
|
vmstate_hpet_timer, HPETTimer),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
|
|
|
@ -156,6 +156,8 @@ typedef struct {
|
||||||
* @modules_supported - whether modules are supported by the machine
|
* @modules_supported - whether modules are supported by the machine
|
||||||
* @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration are
|
* @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration are
|
||||||
* supported by the machine
|
* supported by the machine
|
||||||
|
* @has_caches - whether cache properties are explicitly specified in the
|
||||||
|
* user provided smp-cache configuration
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool prefer_sockets;
|
bool prefer_sockets;
|
||||||
|
@ -166,6 +168,7 @@ typedef struct {
|
||||||
bool drawers_supported;
|
bool drawers_supported;
|
||||||
bool modules_supported;
|
bool modules_supported;
|
||||||
bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX];
|
bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX];
|
||||||
|
bool has_caches;
|
||||||
} SMPCompatProps;
|
} SMPCompatProps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
14
meson.build
14
meson.build
|
@ -821,7 +821,6 @@ version_res = []
|
||||||
coref = []
|
coref = []
|
||||||
iokit = []
|
iokit = []
|
||||||
pvg = not_found
|
pvg = not_found
|
||||||
metal = []
|
|
||||||
emulator_link_args = []
|
emulator_link_args = []
|
||||||
midl = not_found
|
midl = not_found
|
||||||
widl = not_found
|
widl = not_found
|
||||||
|
@ -843,8 +842,8 @@ elif host_os == 'darwin'
|
||||||
coref = dependency('appleframeworks', modules: 'CoreFoundation')
|
coref = dependency('appleframeworks', modules: 'CoreFoundation')
|
||||||
iokit = dependency('appleframeworks', modules: 'IOKit', required: false)
|
iokit = dependency('appleframeworks', modules: 'IOKit', required: false)
|
||||||
host_dsosuf = '.dylib'
|
host_dsosuf = '.dylib'
|
||||||
pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics')
|
pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'],
|
||||||
metal = dependency('appleframeworks', modules: 'Metal')
|
required: get_option('pvg'))
|
||||||
elif host_os == 'sunos'
|
elif host_os == 'sunos'
|
||||||
socket = [cc.find_library('socket'),
|
socket = [cc.find_library('socket'),
|
||||||
cc.find_library('nsl'),
|
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_' + config_target['TARGET_ARCH'].to_upper() + '=y'
|
||||||
target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN']
|
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_input = meson.get_external_property(target, 'default')
|
||||||
config_devices_mak = target + '-config-devices.mak'
|
config_devices_mak = target + '-config-devices.mak'
|
||||||
config_devices_mak = configure_file(
|
config_devices_mak = configure_file(
|
||||||
|
@ -4840,6 +4845,9 @@ summary_info += {'libdw': libdw}
|
||||||
if host_os == 'freebsd'
|
if host_os == 'freebsd'
|
||||||
summary_info += {'libinotify-kqueue': inotify}
|
summary_info += {'libinotify-kqueue': inotify}
|
||||||
endif
|
endif
|
||||||
|
if host_os == 'darwin'
|
||||||
|
summary_info += {'ParavirtualizedGraphics support': pvg}
|
||||||
|
endif
|
||||||
summary(summary_info, bool_yn: true, section: 'Dependencies')
|
summary(summary_info, bool_yn: true, section: 'Dependencies')
|
||||||
|
|
||||||
if host_arch == 'unknown'
|
if host_arch == 'unknown'
|
||||||
|
|
|
@ -198,6 +198,8 @@ option('lzfse', type : 'feature', value : 'auto',
|
||||||
description: 'lzfse support for DMG images')
|
description: 'lzfse support for DMG images')
|
||||||
option('lzo', type : 'feature', value : 'auto',
|
option('lzo', type : 'feature', value : 'auto',
|
||||||
description: 'lzo compression support')
|
description: 'lzo compression support')
|
||||||
|
option('pvg', type: 'feature', value: 'auto',
|
||||||
|
description: 'macOS paravirtualized graphics support')
|
||||||
option('rbd', type : 'feature', value : 'auto',
|
option('rbd', type : 'feature', value : 'auto',
|
||||||
description: 'Ceph block device driver')
|
description: 'Ceph block device driver')
|
||||||
option('opengl', type : 'feature', value : 'auto',
|
option('opengl', type : 'feature', value : 'auto',
|
||||||
|
|
|
@ -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"
|
" aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n"
|
||||||
#endif
|
#endif
|
||||||
" memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n"
|
" 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)
|
QEMU_ARCH_ALL)
|
||||||
SRST
|
SRST
|
||||||
``-machine [type=]name[,prop=value[,...]]``
|
``-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
|
-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
|
ERST
|
||||||
|
|
||||||
DEF("M", HAS_ARG, QEMU_OPTION_M,
|
DEF("M", HAS_ARG, QEMU_OPTION_M,
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-visit-qom.h"
|
#include "qapi/qapi-visit-qom.h"
|
||||||
#include "qobject/qobject.h"
|
#include "qobject/qobject.h"
|
||||||
|
#include "qobject/qbool.h"
|
||||||
#include "qobject/qdict.h"
|
#include "qobject/qdict.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qobject/qjson.h"
|
#include "qobject/qjson.h"
|
||||||
|
#include "qobject/qstring.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
#include "qapi/qobject-output-visitor.h"
|
#include "qapi/qobject-output-visitor.h"
|
||||||
#include "qom/object_interfaces.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);
|
g_string_append(str, description);
|
||||||
}
|
}
|
||||||
if (defval) {
|
if (defval) {
|
||||||
g_autofree char *def_json = g_string_free(qobject_to_json(defval),
|
g_autofree char *def_json = NULL;
|
||||||
false);
|
const char *def;
|
||||||
g_string_append_printf(str, " (default: %s)", def_json);
|
|
||||||
|
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);
|
return g_string_free(str, false);
|
||||||
|
|
7
rust/Cargo.lock
generated
7
rust/Cargo.lock
generated
|
@ -54,6 +54,12 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.162"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pl011"
|
name = "pl011"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -100,6 +106,7 @@ dependencies = [
|
||||||
name = "qemu_api"
|
name = "qemu_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"libc",
|
||||||
"qemu_api_macros",
|
"qemu_api_macros",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use core::ptr::{addr_of, addr_of_mut, NonNull};
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
os::raw::{c_int, c_void},
|
os::raw::{c_int, c_void},
|
||||||
|
ptr::{addr_of, addr_of_mut, NonNull},
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
|
@ -19,8 +19,8 @@ use qemu_api::{
|
||||||
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||||
qom::{ClassInitImpl, ObjectImpl, Owned, ParentField},
|
qom::{ObjectImpl, Owned, ParentField},
|
||||||
sysbus::{SysBusDevice, SysBusDeviceClass},
|
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||||
vmstate::VMStateDescription,
|
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
|
// FIFOs use 32-bit indices instead of usize, for compatibility with
|
||||||
// the migration stream produced by the C version of this device.
|
// the migration stream produced by the C version of this device.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -143,16 +138,24 @@ pub struct PL011Class {
|
||||||
device_id: DeviceId,
|
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 {
|
unsafe impl ObjectType for PL011State {
|
||||||
type Class = PL011Class;
|
type Class = PL011Class;
|
||||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClassInitImpl<PL011Class> for PL011State {
|
impl PL011Impl for PL011State {
|
||||||
fn class_init(klass: &mut PL011Class) {
|
const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
|
||||||
klass.device_id = DeviceId::ARM;
|
|
||||||
<Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PL011State {
|
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_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
||||||
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_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 {
|
impl DeviceImpl for PL011State {
|
||||||
|
@ -176,6 +180,8 @@ impl ResettablePhasesImpl for PL011State {
|
||||||
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
|
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SysBusDeviceImpl for PL011State {}
|
||||||
|
|
||||||
impl PL011Registers {
|
impl PL011Registers {
|
||||||
pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) {
|
pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) {
|
||||||
use RegisterOffset::*;
|
use RegisterOffset::*;
|
||||||
|
@ -726,13 +732,6 @@ pub struct PL011Luminary {
|
||||||
parent_obj: ParentField<PL011State>,
|
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);
|
qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object);
|
||||||
|
|
||||||
unsafe impl ObjectType for PL011Luminary {
|
unsafe impl ObjectType for PL011Luminary {
|
||||||
|
@ -742,7 +741,14 @@ unsafe impl ObjectType for PL011Luminary {
|
||||||
|
|
||||||
impl ObjectImpl for PL011Luminary {
|
impl ObjectImpl for PL011Luminary {
|
||||||
type ParentType = PL011State;
|
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 DeviceImpl for PL011Luminary {}
|
||||||
impl ResettablePhasesImpl for PL011Luminary {}
|
impl ResettablePhasesImpl for PL011Luminary {}
|
||||||
|
impl SysBusDeviceImpl for PL011Luminary {}
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use core::ptr::NonNull;
|
use std::{
|
||||||
use std::os::raw::{c_int, c_void};
|
os::raw::{c_int, c_void},
|
||||||
|
ptr::NonNull,
|
||||||
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct,
|
bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct,
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
config X_HPET_RUST
|
config X_HPET_RUST
|
||||||
bool
|
bool
|
||||||
|
default y if PC && HAVE_RUST
|
||||||
|
|
|
@ -23,7 +23,7 @@ use qemu_api::{
|
||||||
qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||||
qom::{ObjectImpl, ObjectType, ParentField},
|
qom::{ObjectImpl, ObjectType, ParentField},
|
||||||
qom_isa,
|
qom_isa,
|
||||||
sysbus::SysBusDevice,
|
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||||
timer::{Timer, CLOCK_VIRTUAL},
|
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_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
||||||
const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_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!
|
// TODO: Make these properties user-configurable!
|
||||||
|
@ -887,3 +888,5 @@ impl DeviceImpl for HPETState {
|
||||||
impl ResettablePhasesImpl for HPETState {
|
impl ResettablePhasesImpl for HPETState {
|
||||||
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
|
const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SysBusDeviceImpl for HPETState {}
|
||||||
|
|
|
@ -16,6 +16,7 @@ rust-version = "1.63.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
qemu_api_macros = { path = "../qemu-api-macros" }
|
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||||
|
libc = "0.2.162"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
version_check = "~0.9"
|
version_check = "~0.9"
|
||||||
|
|
|
@ -2,6 +2,8 @@ _qemu_api_cfg = run_command(rustc_args,
|
||||||
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
|
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
|
||||||
capture: true, check: true).stdout().strip().splitlines()
|
capture: true, check: true).stdout().strip().splitlines()
|
||||||
|
|
||||||
|
libc_dep = dependency('libc-0.2-rs')
|
||||||
|
|
||||||
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
|
||||||
if rustc.version().version_compare('>=1.77.0')
|
if rustc.version().version_compare('>=1.77.0')
|
||||||
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
||||||
|
@ -22,6 +24,7 @@ _qemu_api_rs = static_library(
|
||||||
'src/cell.rs',
|
'src/cell.rs',
|
||||||
'src/chardev.rs',
|
'src/chardev.rs',
|
||||||
'src/c_str.rs',
|
'src/c_str.rs',
|
||||||
|
'src/errno.rs',
|
||||||
'src/irq.rs',
|
'src/irq.rs',
|
||||||
'src/memory.rs',
|
'src/memory.rs',
|
||||||
'src/module.rs',
|
'src/module.rs',
|
||||||
|
@ -39,6 +42,7 @@ _qemu_api_rs = static_library(
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: _qemu_api_cfg,
|
rust_args: _qemu_api_cfg,
|
||||||
|
dependencies: libc_dep,
|
||||||
)
|
)
|
||||||
|
|
||||||
rust.test('rust-qemu-api-tests', _qemu_api_rs,
|
rust.test('rust-qemu-api-tests', _qemu_api_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)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
345
rust/qemu-api/src/errno.rs
Normal file
345
rust/qemu-api/src/errno.rs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
//! Bindings for interrupt sources
|
//! Bindings for interrupt sources
|
||||||
|
|
||||||
use core::ptr;
|
use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr};
|
||||||
use std::{ffi::CStr, marker::PhantomData, os::raw::c_int};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bindings::{self, qemu_set_irq},
|
bindings::{self, qemu_set_irq},
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub mod c_str;
|
||||||
pub mod callbacks;
|
pub mod callbacks;
|
||||||
pub mod cell;
|
pub mod cell;
|
||||||
pub mod chardev;
|
pub mod chardev;
|
||||||
|
pub mod errno;
|
||||||
pub mod irq;
|
pub mod irq;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
|
|
@ -9,6 +9,8 @@ pub use crate::bitops::IntegerExt;
|
||||||
pub use crate::cell::BqlCell;
|
pub use crate::cell::BqlCell;
|
||||||
pub use crate::cell::BqlRefCell;
|
pub use crate::cell::BqlRefCell;
|
||||||
|
|
||||||
|
pub use crate::errno;
|
||||||
|
|
||||||
pub use crate::qdev::DeviceMethods;
|
pub use crate::qdev::DeviceMethods;
|
||||||
|
|
||||||
pub use crate::qom::InterfaceType;
|
pub use crate::qom::InterfaceType;
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
chardev::Chardev,
|
chardev::Chardev,
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned},
|
qom::{ObjectClass, ObjectImpl, Owned},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait providing the contents of [`DeviceClass`].
|
/// 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
|
/// _Realization_ is the second stage of device creation. It contains
|
||||||
/// all operations that depend on device properties and can fail (note:
|
/// all operations that depend on device properties and can fail (note:
|
||||||
/// this is not yet supported for Rust devices).
|
/// this is not yet supported for Rust devices).
|
||||||
|
@ -113,7 +113,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This function is only called through the QOM machinery and
|
/// 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
|
/// 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
|
/// can be downcasted to type `T`. We also expect the device is
|
||||||
/// readable/writeable from one thread at any time.
|
/// 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) };
|
unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) };
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ClassInitImpl<ResettableClass> for T
|
impl ResettableClass {
|
||||||
where
|
/// Fill in the virtual methods of `ResettableClass` based on the
|
||||||
T: ResettablePhasesImpl,
|
/// definitions in the `ResettablePhasesImpl` trait.
|
||||||
{
|
pub fn class_init<T: ResettablePhasesImpl>(&mut self) {
|
||||||
fn class_init(rc: &mut ResettableClass) {
|
|
||||||
if <T as ResettablePhasesImpl>::ENTER.is_some() {
|
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() {
|
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() {
|
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
|
impl DeviceClass {
|
||||||
where
|
/// Fill in the virtual methods of `DeviceClass` based on the definitions in
|
||||||
T: ClassInitImpl<ObjectClass> + ClassInitImpl<ResettableClass> + DeviceImpl,
|
/// the `DeviceImpl` trait.
|
||||||
{
|
pub fn class_init<T: DeviceImpl>(&mut self) {
|
||||||
fn class_init(dc: &mut DeviceClass) {
|
|
||||||
if <T as DeviceImpl>::REALIZE.is_some() {
|
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() {
|
if let Some(vmsd) = <T as DeviceImpl>::vmsd() {
|
||||||
dc.vmsd = vmsd;
|
self.vmsd = vmsd;
|
||||||
}
|
}
|
||||||
let prop = <T as DeviceImpl>::properties();
|
let prop = <T as DeviceImpl>::properties();
|
||||||
if !prop.is_empty() {
|
if !prop.is_empty() {
|
||||||
unsafe {
|
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);
|
ResettableClass::cast::<DeviceState>(self).class_init::<T>();
|
||||||
<T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class);
|
self.parent_class.class_init::<T>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,8 @@
|
||||||
//! * a trait for virtual method implementations, for example `DeviceImpl`.
|
//! * a trait for virtual method implementations, for example `DeviceImpl`.
|
||||||
//! Child classes implement this trait to provide their own behavior for
|
//! Child classes implement this trait to provide their own behavior for
|
||||||
//! virtual methods. The trait's methods take `&self` to access instance data.
|
//! virtual methods. The trait's methods take `&self` to access instance data.
|
||||||
//!
|
//! The traits have the appropriate specialization of `IsA<>` as a supertrait,
|
||||||
//! * an implementation of [`ClassInitImpl`], for example
|
//! for example `IsA<DeviceState>` for `DeviceImpl`.
|
||||||
//! `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.
|
|
||||||
//!
|
//!
|
||||||
//! * a trait for instance methods, for example `DeviceMethods`. This trait is
|
//! * a trait for instance methods, for example `DeviceMethods`. This trait is
|
||||||
//! automatically implemented for any reference or smart pointer to a device
|
//! 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
|
//! This provides access to class-wide functionality that doesn't depend on
|
||||||
//! instance data. Like instance methods, these are automatically inherited by
|
//! instance data. Like instance methods, these are automatically inherited by
|
||||||
//! child classes.
|
//! 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::{
|
use std::{
|
||||||
ffi::CStr,
|
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() });
|
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,
|
klass: *mut ObjectClass,
|
||||||
_data: *mut c_void,
|
_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>
|
// SAFETY: klass is a T::Class, since rust_class_init<T>
|
||||||
// is called from QOM core as the class_init function
|
// is called from QOM core as the class_init function
|
||||||
// for class T
|
// 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) {
|
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
|
||||||
|
@ -277,19 +316,25 @@ pub unsafe trait InterfaceType: Sized {
|
||||||
/// for this interface.
|
/// for this interface.
|
||||||
const TYPE_NAME: &'static CStr;
|
const TYPE_NAME: &'static CStr;
|
||||||
|
|
||||||
/// Initialize the vtable for the interface; the generic argument `T` is the
|
/// Return the vtable for the interface; `U` is the type that
|
||||||
/// type being initialized, while the generic argument `U` is the type that
|
|
||||||
/// lists the interface in its `TypeInfo`.
|
/// 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
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panic if the incoming argument if `T` does not implement the interface.
|
/// Panic if the incoming argument if `T` does not implement the interface.
|
||||||
fn interface_init<
|
fn cast<U: ObjectType>(klass: &mut U::Class) -> &mut Self {
|
||||||
T: ObjectType + ClassInitImpl<Self> + ClassInitImpl<U::Class>,
|
|
||||||
U: ObjectType,
|
|
||||||
>(
|
|
||||||
klass: &mut U::Class,
|
|
||||||
) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// SAFETY: upcasting to ObjectClass is always valid, and the
|
// SAFETY: upcasting to ObjectClass is always valid, and the
|
||||||
// return type is either NULL or the argument itself
|
// return type is either NULL or the argument itself
|
||||||
|
@ -298,8 +343,7 @@ pub unsafe trait InterfaceType: Sized {
|
||||||
Self::TYPE_NAME.as_ptr(),
|
Self::TYPE_NAME.as_ptr(),
|
||||||
)
|
)
|
||||||
.cast();
|
.cast();
|
||||||
|
result.as_mut().unwrap()
|
||||||
<T as ClassInitImpl<Self>>::class_init(result.as_mut().unwrap())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,7 +541,7 @@ impl<T: ObjectType> ObjectDeref for &mut T {}
|
||||||
impl<T: ObjectType> ObjectCastMut for &mut T {}
|
impl<T: ObjectType> ObjectCastMut for &mut T {}
|
||||||
|
|
||||||
/// Trait a type must implement to be registered with QEMU.
|
/// 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
|
/// The parent of the type. This should match the first field of the
|
||||||
/// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper.
|
/// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper.
|
||||||
type ParentType: ObjectType;
|
type ParentType: ObjectType;
|
||||||
|
@ -550,85 +594,26 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
|
||||||
|
|
||||||
// methods on ObjectClass
|
// methods on ObjectClass
|
||||||
const UNPARENT: Option<fn(&Self)> = None;
|
const UNPARENT: Option<fn(&Self)> = None;
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal trait used to automatically fill in a class struct.
|
/// Store into the argument the virtual method implementations
|
||||||
///
|
|
||||||
/// 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
|
|
||||||
/// for `Self`. On entry, the virtual method pointers are set to
|
/// for `Self`. On entry, the virtual method pointers are set to
|
||||||
/// the default values coming from the parent classes; the function
|
/// the default values coming from the parent classes; the function
|
||||||
/// can change them to override virtual methods of a parent class.
|
/// can change them to override virtual methods of a parent class.
|
||||||
///
|
///
|
||||||
/// The virtual method implementations usually come from another
|
/// Usually defined simply as `Self::Class::class_init::<Self>`;
|
||||||
/// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl)
|
/// however a default implementation cannot be included here, because the
|
||||||
/// when `T` is [`DeviceClass`](crate::qdev::DeviceClass).
|
/// 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
|
/// 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
|
/// zeroed, otherwise it would not be possible to provide the class as a
|
||||||
/// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable)
|
/// `&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
|
/// to T; this is more easily done once Zeroable does not require a manual
|
||||||
/// implementation (Rust 1.75.0).
|
/// implementation (Rust 1.75.0).
|
||||||
fn class_init(klass: &mut T);
|
const CLASS_INIT: fn(&mut Self::Class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -641,13 +626,12 @@ unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) {
|
||||||
T::UNPARENT.unwrap()(unsafe { state.as_ref() });
|
T::UNPARENT.unwrap()(unsafe { state.as_ref() });
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ClassInitImpl<ObjectClass> for T
|
impl ObjectClass {
|
||||||
where
|
/// Fill in the virtual methods of `ObjectClass` based on the definitions in
|
||||||
T: ObjectImpl,
|
/// the `ObjectImpl` trait.
|
||||||
{
|
pub fn class_init<T: ObjectImpl>(&mut self) {
|
||||||
fn class_init(oc: &mut ObjectClass) {
|
|
||||||
if <T as ObjectImpl>::UNPARENT.is_some() {
|
if <T as ObjectImpl>::UNPARENT.is_some() {
|
||||||
oc.unparent = Some(rust_unparent_fn::<T>);
|
self.unparent = Some(rust_unparent_fn::<T>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ use crate::{
|
||||||
irq::{IRQState, InterruptSource},
|
irq::{IRQState, InterruptSource},
|
||||||
memory::MemoryRegion,
|
memory::MemoryRegion,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{DeviceClass, DeviceState},
|
qdev::{DeviceImpl, DeviceState},
|
||||||
qom::{ClassInitImpl, Owned},
|
qom::Owned,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe impl ObjectType for SysBusDevice {
|
unsafe impl ObjectType for SysBusDevice {
|
||||||
|
@ -25,13 +25,14 @@ unsafe impl ObjectType for SysBusDevice {
|
||||||
}
|
}
|
||||||
qom_isa!(SysBusDevice: DeviceState, Object);
|
qom_isa!(SysBusDevice: DeviceState, Object);
|
||||||
|
|
||||||
// TODO: add SysBusDeviceImpl
|
// TODO: add virtual methods
|
||||||
impl<T> ClassInitImpl<SysBusDeviceClass> for T
|
pub trait SysBusDeviceImpl: DeviceImpl + IsA<SysBusDevice> {}
|
||||||
where
|
|
||||||
T: ClassInitImpl<DeviceClass>,
|
impl SysBusDeviceClass {
|
||||||
{
|
/// Fill in the virtual methods of `SysBusDeviceClass` based on the
|
||||||
fn class_init(sdc: &mut SysBusDeviceClass) {
|
/// definitions in the `SysBusDeviceImpl` trait.
|
||||||
<T as ClassInitImpl<DeviceClass>>::class_init(&mut sdc.parent_class);
|
pub fn class_init<T: SysBusDeviceImpl>(self: &mut SysBusDeviceClass) {
|
||||||
|
self.parent_class.class_init::<T>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::*,
|
bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool},
|
||||||
c_str,
|
c_str,
|
||||||
cell::{self, BqlCell},
|
cell::{self, BqlCell},
|
||||||
declare_properties, define_property,
|
declare_properties, define_property,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
|
qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
|
||||||
qom::{ClassInitImpl, ObjectImpl, ParentField},
|
qom::{ObjectImpl, ParentField},
|
||||||
|
sysbus::SysBusDevice,
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
zeroable::Zeroable,
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
@ -40,6 +41,12 @@ pub struct DummyClass {
|
||||||
parent_class: <DeviceState as ObjectType>::Class,
|
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! {
|
declare_properties! {
|
||||||
DUMMY_PROPERTIES,
|
DUMMY_PROPERTIES,
|
||||||
define_property!(
|
define_property!(
|
||||||
|
@ -59,6 +66,7 @@ unsafe impl ObjectType for DummyState {
|
||||||
impl ObjectImpl for DummyState {
|
impl ObjectImpl for DummyState {
|
||||||
type ParentType = DeviceState;
|
type ParentType = DeviceState;
|
||||||
const ABSTRACT: bool = false;
|
const ABSTRACT: bool = false;
|
||||||
|
const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResettablePhasesImpl for DummyState {}
|
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)]
|
#[derive(qemu_api_macros::offsets)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
|
@ -101,20 +101,15 @@ unsafe impl ObjectType for DummyChildState {
|
||||||
impl ObjectImpl for DummyChildState {
|
impl ObjectImpl for DummyChildState {
|
||||||
type ParentType = DummyState;
|
type ParentType = DummyState;
|
||||||
const ABSTRACT: bool = false;
|
const ABSTRACT: bool = false;
|
||||||
|
const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResettablePhasesImpl for DummyChildState {}
|
impl ResettablePhasesImpl for DummyChildState {}
|
||||||
impl DeviceImpl for DummyChildState {}
|
impl DeviceImpl for DummyChildState {}
|
||||||
|
|
||||||
impl ClassInitImpl<DummyClass> for DummyChildState {
|
impl DummyChildClass {
|
||||||
fn class_init(klass: &mut DummyClass) {
|
pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) {
|
||||||
<Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class);
|
self.parent_class.class_init::<T>();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClassInitImpl<DummyChildClass> for DummyChildState {
|
|
||||||
fn class_init(klass: &mut DummyChildClass) {
|
|
||||||
<Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ sub_file="${sub_tdir}/submodule.tar"
|
||||||
# different to the host OS.
|
# different to the host OS.
|
||||||
subprojects="keycodemapdb libvfio-user berkeley-softfloat-3
|
subprojects="keycodemapdb libvfio-user berkeley-softfloat-3
|
||||||
berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs
|
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
|
proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs
|
||||||
syn-2-rs unicode-ident-1-rs"
|
syn-2-rs unicode-ident-1-rs"
|
||||||
sub_deinit=""
|
sub_deinit=""
|
||||||
|
|
|
@ -41,7 +41,7 @@ fi
|
||||||
# Only include wraps that are invoked with subproject()
|
# Only include wraps that are invoked with subproject()
|
||||||
SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3
|
SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3
|
||||||
berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs
|
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
|
proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs
|
||||||
syn-2-rs unicode-ident-1-rs"
|
syn-2-rs unicode-ident-1-rs"
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,7 @@ meson_options_help() {
|
||||||
printf "%s\n" ' pixman pixman support'
|
printf "%s\n" ' pixman pixman support'
|
||||||
printf "%s\n" ' plugins TCG plugins via shared library loading'
|
printf "%s\n" ' plugins TCG plugins via shared library loading'
|
||||||
printf "%s\n" ' png PNG support with libpng'
|
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" ' qatzip QATzip compression support'
|
||||||
printf "%s\n" ' qcow1 qcow1 image format support'
|
printf "%s\n" ' qcow1 qcow1 image format support'
|
||||||
printf "%s\n" ' qed qed image format support'
|
printf "%s\n" ' qed qed image format support'
|
||||||
|
@ -436,6 +437,8 @@ _meson_option_parse() {
|
||||||
--enable-png) printf "%s" -Dpng=enabled ;;
|
--enable-png) printf "%s" -Dpng=enabled ;;
|
||||||
--disable-png) printf "%s" -Dpng=disabled ;;
|
--disable-png) printf "%s" -Dpng=disabled ;;
|
||||||
--prefix=*) quote_sh "-Dprefix=$2" ;;
|
--prefix=*) quote_sh "-Dprefix=$2" ;;
|
||||||
|
--enable-pvg) printf "%s" -Dpvg=enabled ;;
|
||||||
|
--disable-pvg) printf "%s" -Dpvg=disabled ;;
|
||||||
--enable-qatzip) printf "%s" -Dqatzip=enabled ;;
|
--enable-qatzip) printf "%s" -Dqatzip=enabled ;;
|
||||||
--disable-qatzip) printf "%s" -Dqatzip=disabled ;;
|
--disable-qatzip) printf "%s" -Dqatzip=disabled ;;
|
||||||
--enable-qcow1) printf "%s" -Dqcow1=enabled ;;
|
--enable-qcow1) printf "%s" -Dqcow1=enabled ;;
|
||||||
|
|
|
@ -62,7 +62,7 @@ if have_user
|
||||||
stub_ss.add(files('qdev.c'))
|
stub_ss.add(files('qdev.c'))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
stub_ss.add(files('monitor-fd.c'))
|
stub_ss.add(files('monitor-internal.c'))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if have_system
|
if have_system
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
1
subprojects/.gitignore
vendored
1
subprojects/.gitignore
vendored
|
@ -11,6 +11,7 @@
|
||||||
/bilge-impl-0.2.0
|
/bilge-impl-0.2.0
|
||||||
/either-1.12.0
|
/either-1.12.0
|
||||||
/itertools-0.11.0
|
/itertools-0.11.0
|
||||||
|
/libc-0.2.162
|
||||||
/proc-macro-error-1.0.4
|
/proc-macro-error-1.0.4
|
||||||
/proc-macro-error-attr-1.0.4
|
/proc-macro-error-attr-1.0.4
|
||||||
/proc-macro2-1.0.84
|
/proc-macro2-1.0.84
|
||||||
|
|
7
subprojects/libc-0.2-rs.wrap
Normal file
7
subprojects/libc-0.2-rs.wrap
Normal file
|
@ -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
|
37
subprojects/packagefiles/libc-0.2-rs/meson.build
Normal file
37
subprojects/packagefiles/libc-0.2-rs/meson.build
Normal file
|
@ -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)
|
|
@ -1882,7 +1882,11 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||||
if (new_block->flags & RAM_GUEST_MEMFD) {
|
if (new_block->flags & RAM_GUEST_MEMFD) {
|
||||||
int ret;
|
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);
|
assert(new_block->guest_memfd < 0);
|
||||||
|
|
||||||
ret = ram_block_discard_require(true);
|
ret = ram_block_discard_require(true);
|
||||||
|
|
|
@ -247,6 +247,9 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info,
|
||||||
case CPU_TOPOLOGY_LEVEL_CORE:
|
case CPU_TOPOLOGY_LEVEL_CORE:
|
||||||
num_ids = 1 << apicid_core_offset(topo_info);
|
num_ids = 1 << apicid_core_offset(topo_info);
|
||||||
break;
|
break;
|
||||||
|
case CPU_TOPOLOGY_LEVEL_MODULE:
|
||||||
|
num_ids = 1 << apicid_module_offset(topo_info);
|
||||||
|
break;
|
||||||
case CPU_TOPOLOGY_LEVEL_DIE:
|
case CPU_TOPOLOGY_LEVEL_DIE:
|
||||||
num_ids = 1 << apicid_die_offset(topo_info);
|
num_ids = 1 << apicid_die_offset(topo_info);
|
||||||
break;
|
break;
|
||||||
|
@ -255,7 +258,7 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info,
|
||||||
break;
|
break;
|
||||||
default:
|
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.
|
* assert directly to facilitate debugging.
|
||||||
*/
|
*/
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
@ -5495,6 +5498,130 @@ static const X86CPUDefinition builtin_x86_defs[] = {
|
||||||
.model_id = "AMD EPYC-Genoa Processor",
|
.model_id = "AMD EPYC-Genoa Processor",
|
||||||
.cache_info = &epyc_genoa_cache_info,
|
.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
|
* The Linux kernel checks for the CMPLegacy bit and
|
||||||
* discards multiple thread information if it is set.
|
* 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;
|
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;
|
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)
|
static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(dev);
|
CPUState *cs = CPU(dev);
|
||||||
|
@ -8142,6 +8328,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
MachineState *ms = MACHINE(qdev_get_machine());
|
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);
|
qemu_register_reset(x86_cpu_machine_reset_cb, cpu);
|
||||||
|
|
||||||
if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) {
|
if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) {
|
||||||
|
|
|
@ -1110,6 +1110,27 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
|
||||||
/* CPUID[0x80000007].EDX flags: */
|
/* CPUID[0x80000007].EDX flags: */
|
||||||
#define CPUID_APM_INVTSC (1U << 8)
|
#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_SZ 12
|
||||||
|
|
||||||
#define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */
|
#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_3 0x444d4163 /* "cAMD" */
|
||||||
#define CPUID_VENDOR_AMD "AuthenticAMD"
|
#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"
|
#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 && \
|
#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \
|
||||||
(env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
|
(env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
|
||||||
(env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3)
|
(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_IBE (1U << 1) /* Interrupts can exit capability */
|
||||||
#define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */
|
#define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
|
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 */
|
/* Host specific functions */
|
||||||
int hvf_inject_interrupt(CPUArchState *env, int vector);
|
int hvf_inject_interrupt(CPUArchState *env, int vector);
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
#include "vmx.h"
|
#include "vmx.h"
|
||||||
#include "x86.h"
|
#include "x86.h"
|
||||||
#include "x86_descr.h"
|
#include "x86_descr.h"
|
||||||
|
#include "x86_flags.h"
|
||||||
#include "x86_mmu.h"
|
#include "x86_mmu.h"
|
||||||
#include "x86_decode.h"
|
#include "x86_decode.h"
|
||||||
#include "x86_emu.h"
|
#include "x86_emu.h"
|
||||||
|
@ -103,7 +104,7 @@ static void update_apic_tpr(CPUState *cpu)
|
||||||
|
|
||||||
#define VECTORING_INFO_VECTOR_MASK 0xff
|
#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 direction, int size, int count)
|
||||||
{
|
{
|
||||||
int i;
|
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)
|
int hvf_vcpu_exec(CPUState *cpu)
|
||||||
{
|
{
|
||||||
X86CPU *x86_cpu = X86_CPU(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)) {
|
if (ept_emulation_fault(slot, gpa, exit_qual)) {
|
||||||
struct x86_decode decode;
|
struct x86_decode decode;
|
||||||
|
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
decode_instruction(env, &decode);
|
decode_instruction(env, &decode);
|
||||||
exec_instruction(env, &decode);
|
exec_instruction(env, &decode);
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -535,8 +794,8 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
|
|
||||||
if (!string && in) {
|
if (!string && in) {
|
||||||
uint64_t val = 0;
|
uint64_t val = 0;
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
hvf_handle_io(env, port, &val, 0, size, 1);
|
hvf_handle_io(env_cpu(env), port, &val, 0, size, 1);
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
AL(env) = val;
|
AL(env) = val;
|
||||||
} else if (size == 2) {
|
} else if (size == 2) {
|
||||||
|
@ -547,21 +806,21 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
RAX(env) = (uint64_t)val;
|
RAX(env) = (uint64_t)val;
|
||||||
}
|
}
|
||||||
env->eip += ins_len;
|
env->eip += ins_len;
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
break;
|
break;
|
||||||
} else if (!string && !in) {
|
} else if (!string && !in) {
|
||||||
RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX);
|
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);
|
macvm_set_rip(cpu, rip + ins_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
struct x86_decode decode;
|
struct x86_decode decode;
|
||||||
|
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
decode_instruction(env, &decode);
|
decode_instruction(env, &decode);
|
||||||
assert(ins_len == decode.len);
|
assert(ins_len == decode.len);
|
||||||
exec_instruction(env, &decode);
|
exec_instruction(env, &decode);
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -614,21 +873,21 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
case EXIT_REASON_RDMSR:
|
case EXIT_REASON_RDMSR:
|
||||||
case EXIT_REASON_WRMSR:
|
case EXIT_REASON_WRMSR:
|
||||||
{
|
{
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
if (exit_reason == EXIT_REASON_RDMSR) {
|
if (exit_reason == EXIT_REASON_RDMSR) {
|
||||||
simulate_rdmsr(env);
|
hvf_simulate_rdmsr(env);
|
||||||
} else {
|
} else {
|
||||||
simulate_wrmsr(env);
|
hvf_simulate_wrmsr(env);
|
||||||
}
|
}
|
||||||
env->eip += ins_len;
|
env->eip += ins_len;
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXIT_REASON_CR_ACCESS: {
|
case EXIT_REASON_CR_ACCESS: {
|
||||||
int cr;
|
int cr;
|
||||||
int reg;
|
int reg;
|
||||||
|
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
cr = exit_qual & 15;
|
cr = exit_qual & 15;
|
||||||
reg = (exit_qual >> 8) & 15;
|
reg = (exit_qual >> 8) & 15;
|
||||||
|
|
||||||
|
@ -656,16 +915,16 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
env->eip += ins_len;
|
env->eip += ins_len;
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXIT_REASON_APIC_ACCESS: { /* TODO */
|
case EXIT_REASON_APIC_ACCESS: { /* TODO */
|
||||||
struct x86_decode decode;
|
struct x86_decode decode;
|
||||||
|
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
decode_instruction(env, &decode);
|
decode_instruction(env, &decode);
|
||||||
exec_instruction(env, &decode);
|
exec_instruction(env, &decode);
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXIT_REASON_TPR: {
|
case EXIT_REASON_TPR: {
|
||||||
|
@ -674,7 +933,7 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
}
|
}
|
||||||
case EXIT_REASON_TASK_SWITCH: {
|
case EXIT_REASON_TASK_SWITCH: {
|
||||||
uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO);
|
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,
|
vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3,
|
||||||
vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo
|
vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo
|
||||||
& VMCS_INTR_T_MASK);
|
& VMCS_INTR_T_MASK);
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
bool x86_read_segment_descriptor(CPUState *cpu,
|
bool x86_read_segment_descriptor(CPUState *cpu,
|
||||||
struct x86_segment_descriptor *desc,
|
struct x86_segment_descriptor *desc,
|
||||||
x68_segment_selector sel)
|
x86_segment_selector sel)
|
||||||
{
|
{
|
||||||
target_ulong base;
|
target_ulong base;
|
||||||
uint32_t limit;
|
uint32_t limit;
|
||||||
|
@ -78,7 +78,7 @@ bool x86_read_segment_descriptor(CPUState *cpu,
|
||||||
|
|
||||||
bool x86_write_segment_descriptor(CPUState *cpu,
|
bool x86_write_segment_descriptor(CPUState *cpu,
|
||||||
struct x86_segment_descriptor *desc,
|
struct x86_segment_descriptor *desc,
|
||||||
x68_segment_selector sel)
|
x86_segment_selector sel)
|
||||||
{
|
{
|
||||||
target_ulong base;
|
target_ulong base;
|
||||||
uint32_t limit;
|
uint32_t limit;
|
||||||
|
|
|
@ -183,7 +183,7 @@ static inline uint32_t x86_call_gate_offset(x86_call_gate *gate)
|
||||||
#define GDT_SEL 0
|
#define GDT_SEL 0
|
||||||
#define LDT_SEL 1
|
#define LDT_SEL 1
|
||||||
|
|
||||||
typedef struct x68_segment_selector {
|
typedef struct x86_segment_selector {
|
||||||
union {
|
union {
|
||||||
uint16_t sel;
|
uint16_t sel;
|
||||||
struct {
|
struct {
|
||||||
|
@ -192,7 +192,7 @@ typedef struct x68_segment_selector {
|
||||||
uint16_t index:13;
|
uint16_t index:13;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} __attribute__ ((__packed__)) x68_segment_selector;
|
} __attribute__ ((__packed__)) x86_segment_selector;
|
||||||
|
|
||||||
/* useful register access macros */
|
/* useful register access macros */
|
||||||
#define x86_reg(cpu, reg) ((x86_register *) &cpu->regs[reg])
|
#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 */
|
/* deal with GDT/LDT descriptors in memory */
|
||||||
bool x86_read_segment_descriptor(CPUState *cpu,
|
bool x86_read_segment_descriptor(CPUState *cpu,
|
||||||
struct x86_segment_descriptor *desc,
|
struct x86_segment_descriptor *desc,
|
||||||
x68_segment_selector sel);
|
x86_segment_selector sel);
|
||||||
bool x86_write_segment_descriptor(CPUState *cpu,
|
bool x86_write_segment_descriptor(CPUState *cpu,
|
||||||
struct x86_segment_descriptor *desc,
|
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,
|
bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc,
|
||||||
int gate);
|
int gate);
|
||||||
|
|
|
@ -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)
|
void set_addressing_size(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->addressing_size = -1;
|
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))) {
|
} else if (!x86_is_long_mode(env_cpu(env))) {
|
||||||
/* protected */
|
/* protected */
|
||||||
struct vmx_segment cs;
|
x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env));
|
||||||
vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS);
|
|
||||||
/* check db */
|
/* check db */
|
||||||
if ((cs.ar >> 14) & 1) {
|
if (cs.db) {
|
||||||
if (decode->addr_size_override) {
|
if (decode->addr_size_override) {
|
||||||
decode->addressing_size = 2;
|
decode->addressing_size = 2;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1941,10 +1950,9 @@ void set_operand_size(CPUX86State *env, struct x86_decode *decode)
|
||||||
}
|
}
|
||||||
} else if (!x86_is_long_mode(env_cpu(env))) {
|
} else if (!x86_is_long_mode(env_cpu(env))) {
|
||||||
/* protected */
|
/* protected */
|
||||||
struct vmx_segment cs;
|
x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env));
|
||||||
vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS);
|
|
||||||
/* check db */
|
/* check db */
|
||||||
if ((cs.ar >> 14) & 1) {
|
if (cs.db) {
|
||||||
if (decode->op_size_override) {
|
if (decode->op_size_override) {
|
||||||
decode->operand_size = 2;
|
decode->operand_size = 2;
|
||||||
} else{
|
} else{
|
||||||
|
|
|
@ -60,14 +60,14 @@ uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg)
|
||||||
return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base);
|
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);
|
sel.sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector);
|
||||||
return sel;
|
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);
|
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);
|
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 x86_segment_descriptor *desc,
|
||||||
struct vmx_segment *vmx_desc)
|
struct vmx_segment *vmx_desc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,10 +34,10 @@ void vmx_read_segment_descriptor(CPUState *cpu,
|
||||||
void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc,
|
void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc,
|
||||||
enum X86Seg seg);
|
enum X86Seg seg);
|
||||||
|
|
||||||
x68_segment_selector vmx_read_segment_selector(CPUState *cpu,
|
x86_segment_selector vmx_read_segment_selector(CPUState *cpu,
|
||||||
enum X86Seg seg);
|
enum X86Seg seg);
|
||||||
void vmx_write_segment_selector(CPUState *cpu,
|
void vmx_write_segment_selector(CPUState *cpu,
|
||||||
x68_segment_selector selector,
|
x86_segment_selector selector,
|
||||||
enum X86Seg seg);
|
enum X86Seg seg);
|
||||||
|
|
||||||
uint64_t vmx_read_segment_base(CPUState *cpu, 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);
|
uint64_t base);
|
||||||
|
|
||||||
void x86_segment_descriptor_to_vmx(CPUState *cpu,
|
void x86_segment_descriptor_to_vmx(CPUState *cpu,
|
||||||
x68_segment_selector selector,
|
x86_segment_selector selector,
|
||||||
struct x86_segment_descriptor *desc,
|
struct x86_segment_descriptor *desc,
|
||||||
struct vmx_segment *vmx_desc);
|
struct vmx_segment *vmx_desc);
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,7 @@
|
||||||
#include "x86_flags.h"
|
#include "x86_flags.h"
|
||||||
#include "vmcs.h"
|
#include "vmcs.h"
|
||||||
#include "vmx.h"
|
#include "vmx.h"
|
||||||
|
#include "hvf-i386.h"
|
||||||
void hvf_handle_io(CPUState *cs, uint16_t port, void *data,
|
|
||||||
int direction, int size, uint32_t count);
|
|
||||||
|
|
||||||
#define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \
|
#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;
|
env->eip += decode->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raise_exception(CPUX86State *env, int exception_index,
|
void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code)
|
||||||
int error_code)
|
|
||||||
{
|
{
|
||||||
env->exception_nr = exception_index;
|
env->exception_nr = exception_index;
|
||||||
env->error_code = error_code;
|
env->error_code = error_code;
|
||||||
|
@ -672,227 +669,15 @@ static void raise_exception(CPUX86State *env, int exception_index,
|
||||||
env->exception_injected = 1;
|
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)
|
static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
simulate_rdmsr(env);
|
hvf_simulate_rdmsr(env);
|
||||||
env->eip += decode->len;
|
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)
|
static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
simulate_wrmsr(env);
|
hvf_simulate_wrmsr(env);
|
||||||
env->eip += decode->len;
|
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)
|
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) {
|
if (!_cmd_handler[ins->cmd].handler) {
|
||||||
printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip,
|
printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip,
|
||||||
ins->cmd, ins->opcode[0],
|
ins->cmd, ins->opcode[0],
|
||||||
|
|
|
@ -25,12 +25,7 @@
|
||||||
|
|
||||||
void init_emu(void);
|
void init_emu(void);
|
||||||
bool exec_instruction(CPUX86State *env, struct x86_decode *ins);
|
bool exec_instruction(CPUX86State *env, struct x86_decode *ins);
|
||||||
|
void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code);
|
||||||
void load_regs(CPUState *cpu);
|
|
||||||
void store_regs(CPUState *cpu);
|
|
||||||
|
|
||||||
void simulate_rdmsr(CPUX86State *env);
|
|
||||||
void simulate_wrmsr(CPUX86State *env);
|
|
||||||
|
|
||||||
target_ulong read_reg(CPUX86State *env, int reg, int size);
|
target_ulong read_reg(CPUX86State *env, int reg, int size);
|
||||||
void write_reg(CPUX86State *env, int reg, target_ulong val, int size);
|
void write_reg(CPUX86State *env, int reg, target_ulong val, int size);
|
||||||
|
|
|
@ -76,16 +76,16 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss)
|
||||||
RSI(env) = tss->esi;
|
RSI(env) = tss->esi;
|
||||||
RDI(env) = tss->edi;
|
RDI(env) = tss->edi;
|
||||||
|
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR);
|
vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ldt}}, R_LDTR);
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES);
|
vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->es}}, R_ES);
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS);
|
vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->cs}}, R_CS);
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS);
|
vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ss}}, R_SS);
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS);
|
vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ds}}, R_DS);
|
||||||
vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS);
|
vmx_write_segment_selector(cpu, (x86_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->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)
|
uint64_t old_tss_base, struct x86_segment_descriptor *new_desc)
|
||||||
{
|
{
|
||||||
struct x86_tss_segment32 tss_seg;
|
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;
|
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);
|
uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP);
|
||||||
if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION &&
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
load_regs(cpu);
|
hvf_load_regs(cpu);
|
||||||
|
|
||||||
struct x86_segment_descriptor curr_tss_desc, next_tss_desc;
|
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);
|
uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR);
|
||||||
uint32_t desc_limit;
|
uint32_t desc_limit;
|
||||||
struct x86_call_gate task_gate_desc;
|
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);
|
x86_read_call_gate(cpu, &task_gate_desc, gate);
|
||||||
|
|
||||||
dpl = task_gate_desc.dpl;
|
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)
|
if (tss_sel.rpl > dpl || cs.rpl > dpl)
|
||||||
;//DPRINTF("emulate_gp");
|
;//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);
|
x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg);
|
||||||
vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR);
|
vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR);
|
||||||
|
|
||||||
store_regs(cpu);
|
hvf_store_regs(cpu);
|
||||||
|
|
||||||
hv_vcpu_invalidate_tlb(cpu->accel->fd);
|
hv_vcpu_invalidate_tlb(cpu->accel->fd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
#ifndef HVF_X86_TASK_H
|
#ifndef HVF_X86_TASK_H
|
||||||
#define 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);
|
int reason, bool gate_valid, uint8_t gate, uint64_t gate_type);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,4 +31,7 @@ void hvf_get_xsave(CPUState *cs);
|
||||||
void hvf_get_msrs(CPUState *cs);
|
void hvf_get_msrs(CPUState *cs);
|
||||||
void vmx_clear_int_window_exiting(CPUState *cs);
|
void vmx_clear_int_window_exiting(CPUState *cs);
|
||||||
void vmx_update_tpr(CPUState *cs);
|
void vmx_update_tpr(CPUState *cs);
|
||||||
|
|
||||||
|
void hvf_load_regs(CPUState *cpu);
|
||||||
|
void hvf_store_regs(CPUState *cpu);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -700,13 +700,6 @@ static void rv128_base_cpu_init(Object *obj)
|
||||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||||
CPURISCVState *env = &cpu->env;
|
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.mmu = true;
|
||||||
cpu->cfg.pmp = true;
|
cpu->cfg.pmp = true;
|
||||||
|
|
||||||
|
@ -3051,15 +3044,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename)
|
||||||
}
|
}
|
||||||
#endif
|
#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) \
|
#define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \
|
||||||
{ \
|
{ \
|
||||||
.name = (type_name), \
|
.name = (type_name), \
|
||||||
|
|
|
@ -1014,6 +1014,7 @@ static bool riscv_cpu_is_generic(Object *cpu_obj)
|
||||||
static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp)
|
static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp)
|
||||||
{
|
{
|
||||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||||
|
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
if (!riscv_cpu_tcg_compatible(cpu)) {
|
if (!riscv_cpu_tcg_compatible(cpu)) {
|
||||||
g_autofree char *name = riscv_cpu_get_name(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;
|
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
|
#ifndef CONFIG_USER_ONLY
|
||||||
CPURISCVState *env = &cpu->env;
|
CPURISCVState *env = &cpu->env;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue