mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 00:33:55 -06:00
* rust: miscellaneous changes
* target/i386: small code generation improvements * target/i386: various cleanups and fixes * cpu: remove env->nr_cores -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmeBoIgUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroOD2gf+NK7U1EhNIrsbBsbtu2i7+tnbRKIB MTu+Mxb2wz4C7//pxq+vva4bgT3iOuL9RF19PRe/63CMD65xMiwyyNrEWX2HbRIJ 5dytLLLdef3yMhHh2x1uZfm54g12Ppvn9kulMCbPawrlqWgg1sZbkUBrRtFzS45c NeYjGWWSpBDe7LtsrgSRYLPnz6wWEiy3tDpu2VoDtjrE86UVDXwyzpbtBk9Y8jPi CKdvLyQeO9xDE5OoXMjJMlJeQq3D9iwYEprXUqy+RUZtpW7YmqMCf2JQ4dAjVCad 07v/kITF4brGCVnzDcDA6W7LqHpBu1w+Hn23yLw3HEDDBt11o9JjQCl9qA== =xIQ4 -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * rust: miscellaneous changes * target/i386: small code generation improvements * target/i386: various cleanups and fixes * cpu: remove env->nr_cores # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmeBoIgUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOD2gf+NK7U1EhNIrsbBsbtu2i7+tnbRKIB # MTu+Mxb2wz4C7//pxq+vva4bgT3iOuL9RF19PRe/63CMD65xMiwyyNrEWX2HbRIJ # 5dytLLLdef3yMhHh2x1uZfm54g12Ppvn9kulMCbPawrlqWgg1sZbkUBrRtFzS45c # NeYjGWWSpBDe7LtsrgSRYLPnz6wWEiy3tDpu2VoDtjrE86UVDXwyzpbtBk9Y8jPi # CKdvLyQeO9xDE5OoXMjJMlJeQq3D9iwYEprXUqy+RUZtpW7YmqMCf2JQ4dAjVCad # 07v/kITF4brGCVnzDcDA6W7LqHpBu1w+Hn23yLw3HEDDBt11o9JjQCl9qA== # =xIQ4 # -----END PGP SIGNATURE----- # gpg: Signature made Fri 10 Jan 2025 17:34:48 EST # 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: (38 commits) i386/cpu: Set and track CPUID_EXT3_CMP_LEG in env->features[FEAT_8000_0001_ECX] i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid() cpu: Remove nr_cores from struct CPUState i386/cpu: Hoist check of CPUID_EXT3_TOPOEXT against threads_per_core i386/cpu: Track a X86CPUTopoInfo directly in CPUX86State i386/topology: Introduce helpers for various topology info of different level i386/topology: Update the comment of x86_apicid_from_topo_ids() i386/cpu: Drop cores_per_pkg in cpu_x86_cpuid() i386/cpu: Drop the variable smp_cores and smp_threads in x86_cpu_pre_plug() i386/cpu: Extract a common fucntion to setup value of MSR_CORE_THREAD_COUNT target/i386/kvm: Replace ARRAY_SIZE(msr_handlers) with KVM_MSR_FILTER_MAX_RANGES target/i386/kvm: Clean up error handling in kvm_arch_init() target/i386/kvm: Return -1 when kvm_msr_energy_thread_init() fails target/i386/kvm: Clean up return values of MSR filter related functions target/i386/confidential-guest: Fix comment of x86_confidential_guest_kvm_type() target/i386/kvm: Drop workaround for KVM_X86_DISABLE_EXITS_HTL typo target/i386/kvm: Only save/load kvmclock MSRs when kvmclock enabled target/i386/kvm: Remove local MSR_KVM_WALL_CLOCK and MSR_KVM_SYSTEM_TIME definitions target/i386/kvm: Add feature bit definitions for KVM CPUID i386/cpu: Mark avx10_version filtered when prefix is NULL ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
09360a048b
52 changed files with 712 additions and 367 deletions
|
@ -243,7 +243,6 @@ static void cpu_common_initfn(Object *obj)
|
||||||
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
|
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
|
||||||
/* user-mode doesn't have configurable SMP topology */
|
/* user-mode doesn't have configurable SMP topology */
|
||||||
/* the default value is changed by qemu_init_vcpu() for system-mode */
|
/* the default value is changed by qemu_init_vcpu() for system-mode */
|
||||||
cpu->nr_cores = 1;
|
|
||||||
cpu->nr_threads = 1;
|
cpu->nr_threads = 1;
|
||||||
cpu->cflags_next_tb = -1;
|
cpu->cflags_next_tb = -1;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#include <linux/kvm.h>
|
#include <linux/kvm.h>
|
||||||
#include "standard-headers/asm-x86/kvm_para.h"
|
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
|
||||||
#define TYPE_KVM_CLOCK "kvmclock"
|
#define TYPE_KVM_CLOCK "kvmclock"
|
||||||
|
@ -333,8 +332,8 @@ void kvmclock_create(bool create_always)
|
||||||
|
|
||||||
assert(kvm_enabled());
|
assert(kvm_enabled());
|
||||||
if (create_always ||
|
if (create_always ||
|
||||||
cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) |
|
cpu->env.features[FEAT_KVM] & (CPUID_KVM_CLOCK |
|
||||||
(1ULL << KVM_FEATURE_CLOCKSOURCE2))) {
|
CPUID_KVM_CLOCK2)) {
|
||||||
sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL);
|
sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,9 +248,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||||
CPUX86State *env = &cpu->env;
|
CPUX86State *env = &cpu->env;
|
||||||
MachineState *ms = MACHINE(hotplug_dev);
|
MachineState *ms = MACHINE(hotplug_dev);
|
||||||
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
|
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
|
||||||
unsigned int smp_cores = ms->smp.cores;
|
X86CPUTopoInfo *topo_info = &env->topo_info;
|
||||||
unsigned int smp_threads = ms->smp.threads;
|
|
||||||
X86CPUTopoInfo topo_info;
|
|
||||||
|
|
||||||
if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
|
if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
|
||||||
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
|
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
|
||||||
|
@ -269,15 +267,13 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init_topo_info(&topo_info, x86ms);
|
init_topo_info(topo_info, x86ms);
|
||||||
|
|
||||||
if (ms->smp.modules > 1) {
|
if (ms->smp.modules > 1) {
|
||||||
env->nr_modules = ms->smp.modules;
|
|
||||||
set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo);
|
set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ms->smp.dies > 1) {
|
if (ms->smp.dies > 1) {
|
||||||
env->nr_dies = ms->smp.dies;
|
|
||||||
set_bit(CPU_TOPOLOGY_LEVEL_DIE, env->avail_cpu_topo);
|
set_bit(CPU_TOPOLOGY_LEVEL_DIE, env->avail_cpu_topo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,17 +325,17 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||||
if (cpu->core_id < 0) {
|
if (cpu->core_id < 0) {
|
||||||
error_setg(errp, "CPU core-id is not set");
|
error_setg(errp, "CPU core-id is not set");
|
||||||
return;
|
return;
|
||||||
} else if (cpu->core_id > (smp_cores - 1)) {
|
} else if (cpu->core_id > (ms->smp.cores - 1)) {
|
||||||
error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
|
error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
|
||||||
cpu->core_id, smp_cores - 1);
|
cpu->core_id, ms->smp.cores - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cpu->thread_id < 0) {
|
if (cpu->thread_id < 0) {
|
||||||
error_setg(errp, "CPU thread-id is not set");
|
error_setg(errp, "CPU thread-id is not set");
|
||||||
return;
|
return;
|
||||||
} else if (cpu->thread_id > (smp_threads - 1)) {
|
} else if (cpu->thread_id > (ms->smp.threads - 1)) {
|
||||||
error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
|
error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
|
||||||
cpu->thread_id, smp_threads - 1);
|
cpu->thread_id, ms->smp.threads - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,12 +344,12 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||||
topo_ids.module_id = cpu->module_id;
|
topo_ids.module_id = cpu->module_id;
|
||||||
topo_ids.core_id = cpu->core_id;
|
topo_ids.core_id = cpu->core_id;
|
||||||
topo_ids.smt_id = cpu->thread_id;
|
topo_ids.smt_id = cpu->thread_id;
|
||||||
cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
|
cpu->apic_id = x86_apicid_from_topo_ids(topo_info, &topo_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
|
cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
|
||||||
if (!cpu_slot) {
|
if (!cpu_slot) {
|
||||||
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
|
x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids);
|
||||||
|
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]"
|
"Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]"
|
||||||
|
@ -376,7 +372,7 @@ void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
||||||
/* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
|
/* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
|
||||||
* once -smp refactoring is complete and there will be CPU private
|
* once -smp refactoring is complete and there will be CPU private
|
||||||
* CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
|
* CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
|
||||||
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
|
x86_topo_ids_from_apicid(cpu->apic_id, topo_info, &topo_ids);
|
||||||
if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {
|
if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {
|
||||||
error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
|
error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
|
||||||
" 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,
|
" 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,
|
||||||
|
|
|
@ -407,7 +407,6 @@ struct qemu_work_item;
|
||||||
* Under TCG this value is propagated to @tcg_cflags.
|
* Under TCG this value is propagated to @tcg_cflags.
|
||||||
* See TranslationBlock::TCG CF_CLUSTER_MASK.
|
* See TranslationBlock::TCG CF_CLUSTER_MASK.
|
||||||
* @tcg_cflags: Pre-computed cflags for this cpu.
|
* @tcg_cflags: Pre-computed cflags for this cpu.
|
||||||
* @nr_cores: Number of cores within this CPU package.
|
|
||||||
* @nr_threads: Number of threads within this CPU core.
|
* @nr_threads: Number of threads within this CPU core.
|
||||||
* @thread: Host thread details, only live once @created is #true
|
* @thread: Host thread details, only live once @created is #true
|
||||||
* @sem: WIN32 only semaphore used only for qtest
|
* @sem: WIN32 only semaphore used only for qtest
|
||||||
|
@ -466,7 +465,6 @@ struct CPUState {
|
||||||
CPUClass *cc;
|
CPUClass *cc;
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
|
|
||||||
int nr_cores;
|
|
||||||
int nr_threads;
|
int nr_threads;
|
||||||
|
|
||||||
struct QemuThread *thread;
|
struct QemuThread *thread;
|
||||||
|
|
|
@ -121,9 +121,10 @@ static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID
|
* Make APIC ID for the CPU based on topology and IDs of each topology level.
|
||||||
*
|
*
|
||||||
* The caller must make sure core_id < nr_cores and smt_id < nr_threads.
|
* The caller must make sure the ID of each level doesn't exceed the width of
|
||||||
|
* the level.
|
||||||
*/
|
*/
|
||||||
static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info,
|
static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info,
|
||||||
const X86CPUTopoIDs *topo_ids)
|
const X86CPUTopoIDs *topo_ids)
|
||||||
|
@ -202,4 +203,29 @@ static inline bool x86_has_extended_topo(unsigned long *topo_bitmap)
|
||||||
test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap);
|
test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned x86_module_per_pkg(X86CPUTopoInfo *topo_info)
|
||||||
|
{
|
||||||
|
return topo_info->modules_per_die * topo_info->dies_per_pkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned x86_cores_per_pkg(X86CPUTopoInfo *topo_info)
|
||||||
|
{
|
||||||
|
return topo_info->cores_per_module * x86_module_per_pkg(topo_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned x86_threads_per_pkg(X86CPUTopoInfo *topo_info)
|
||||||
|
{
|
||||||
|
return topo_info->threads_per_core * x86_cores_per_pkg(topo_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned x86_threads_per_module(X86CPUTopoInfo *topo_info)
|
||||||
|
{
|
||||||
|
return topo_info->threads_per_core * topo_info->cores_per_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned x86_threads_per_die(X86CPUTopoInfo *topo_info)
|
||||||
|
{
|
||||||
|
return x86_threads_per_module(topo_info) * topo_info->modules_per_die;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* HW_I386_TOPOLOGY_H */
|
#endif /* HW_I386_TOPOLOGY_H */
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use core::ptr::{addr_of_mut, NonNull};
|
use core::ptr::{addr_of_mut, NonNull};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
os::raw::{c_int, c_uchar, c_uint, c_void},
|
os::raw::{c_int, c_uint, c_void},
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
|
@ -14,7 +14,7 @@ use qemu_api::{
|
||||||
irq::InterruptSource,
|
irq::InterruptSource,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::DeviceImpl,
|
qdev::DeviceImpl,
|
||||||
qom::ObjectImpl,
|
qom::{ClassInitImpl, ObjectImpl, ParentField},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -33,27 +33,20 @@ const FBRD_MASK: u32 = 0x3f;
|
||||||
/// QEMU sourced constant.
|
/// QEMU sourced constant.
|
||||||
pub const PL011_FIFO_DEPTH: u32 = 16;
|
pub const PL011_FIFO_DEPTH: u32 = 16;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy)]
|
||||||
enum DeviceId {
|
struct DeviceId(&'static [u8; 8]);
|
||||||
#[allow(dead_code)]
|
|
||||||
Arm = 0,
|
|
||||||
Luminary,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Index<hwaddr> for DeviceId {
|
impl std::ops::Index<hwaddr> for DeviceId {
|
||||||
type Output = c_uchar;
|
type Output = u8;
|
||||||
|
|
||||||
fn index(&self, idx: hwaddr) -> &Self::Output {
|
fn index(&self, idx: hwaddr) -> &Self::Output {
|
||||||
match self {
|
&self.0[idx as usize]
|
||||||
Self::Arm => &Self::PL011_ID_ARM[idx as usize],
|
|
||||||
Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceId {
|
impl DeviceId {
|
||||||
const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
|
const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
|
||||||
const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 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
|
||||||
|
@ -86,7 +79,7 @@ impl std::ops::Index<u32> for Fifo {
|
||||||
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
|
||||||
/// PL011 Device Model in QEMU
|
/// PL011 Device Model in QEMU
|
||||||
pub struct PL011State {
|
pub struct PL011State {
|
||||||
pub parent_obj: SysBusDevice,
|
pub parent_obj: ParentField<SysBusDevice>,
|
||||||
pub iomem: MemoryRegion,
|
pub iomem: MemoryRegion,
|
||||||
#[doc(alias = "fr")]
|
#[doc(alias = "fr")]
|
||||||
pub flags: registers::Flags,
|
pub flags: registers::Flags,
|
||||||
|
@ -126,21 +119,33 @@ pub struct PL011State {
|
||||||
pub clock: NonNull<Clock>,
|
pub clock: NonNull<Clock>,
|
||||||
#[doc(alias = "migrate_clk")]
|
#[doc(alias = "migrate_clk")]
|
||||||
pub migrate_clock: bool,
|
pub migrate_clock: bool,
|
||||||
/// The byte string that identifies the device.
|
|
||||||
device_id: DeviceId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
|
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
|
||||||
|
|
||||||
|
pub struct PL011Class {
|
||||||
|
parent_class: <SysBusDevice as ObjectType>::Class,
|
||||||
|
/// The byte string that identifies the device.
|
||||||
|
device_id: DeviceId,
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl ObjectType for PL011State {
|
unsafe impl ObjectType for PL011State {
|
||||||
type Class = <SysBusDevice as ObjectType>::Class;
|
type Class = PL011Class;
|
||||||
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClassInitImpl<PL011Class> for PL011State {
|
||||||
|
fn class_init(klass: &mut PL011Class) {
|
||||||
|
klass.device_id = DeviceId::ARM;
|
||||||
|
<Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PL011State {
|
impl ObjectImpl for PL011State {
|
||||||
type ParentType = SysBusDevice;
|
type ParentType = SysBusDevice;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceImpl for PL011State {
|
impl DeviceImpl for PL011State {
|
||||||
|
@ -179,14 +184,6 @@ impl PL011State {
|
||||||
Self::TYPE_NAME.as_ptr(),
|
Self::TYPE_NAME.as_ptr(),
|
||||||
0x1000,
|
0x1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
let sbd: &mut SysBusDevice = self.upcast_mut();
|
|
||||||
sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
|
|
||||||
}
|
|
||||||
|
|
||||||
for irq in self.interrupts.iter() {
|
|
||||||
let sbd: &SysBusDevice = self.upcast();
|
|
||||||
sbd.init_irq(irq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
|
@ -209,12 +206,20 @@ impl PL011State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn post_init(&self) {
|
||||||
|
self.init_mmio(&self.iomem);
|
||||||
|
for irq in self.interrupts.iter() {
|
||||||
|
self.init_irq(irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> {
|
pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> {
|
||||||
use RegisterOffset::*;
|
use RegisterOffset::*;
|
||||||
|
|
||||||
let value = match RegisterOffset::try_from(offset) {
|
let value = match RegisterOffset::try_from(offset) {
|
||||||
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
|
Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
|
||||||
u32::from(self.device_id[(offset - 0xfe0) >> 2])
|
let device_id = self.get_class().device_id;
|
||||||
|
u32::from(device_id[(offset - 0xfe0) >> 2])
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
// qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
|
||||||
|
@ -645,19 +650,13 @@ pub unsafe extern "C" fn pl011_create(
|
||||||
#[derive(Debug, qemu_api_macros::Object)]
|
#[derive(Debug, qemu_api_macros::Object)]
|
||||||
/// PL011 Luminary device model.
|
/// PL011 Luminary device model.
|
||||||
pub struct PL011Luminary {
|
pub struct PL011Luminary {
|
||||||
parent_obj: PL011State,
|
parent_obj: ParentField<PL011State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PL011Luminary {
|
impl ClassInitImpl<PL011Class> for PL011Luminary {
|
||||||
/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
|
fn class_init(klass: &mut PL011Class) {
|
||||||
///
|
klass.device_id = DeviceId::LUMINARY;
|
||||||
/// # Safety
|
<Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class);
|
||||||
///
|
|
||||||
/// We expect the FFI user of this function to pass a valid pointer, that
|
|
||||||
/// has the same size as [`PL011Luminary`]. We also expect the device is
|
|
||||||
/// readable/writeable from one thread at any time.
|
|
||||||
unsafe fn init(&mut self) {
|
|
||||||
self.parent_obj.device_id = DeviceId::Luminary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,8 +669,6 @@ unsafe impl ObjectType for PL011Luminary {
|
||||||
|
|
||||||
impl ObjectImpl for PL011Luminary {
|
impl ObjectImpl for PL011Luminary {
|
||||||
type ParentType = PL011State;
|
type ParentType = PL011State;
|
||||||
|
|
||||||
const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceImpl for PL011Luminary {}
|
impl DeviceImpl for PL011Luminary {}
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary");
|
||||||
#[doc(alias = "offset")]
|
#[doc(alias = "offset")]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, qemu_api_macros::TryInto)]
|
||||||
pub enum RegisterOffset {
|
pub enum RegisterOffset {
|
||||||
/// Data Register
|
/// Data Register
|
||||||
///
|
///
|
||||||
|
@ -102,32 +102,6 @@ pub enum RegisterOffset {
|
||||||
//Reserved = 0x04C,
|
//Reserved = 0x04C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::convert::TryFrom<u64> for RegisterOffset {
|
|
||||||
type Error = u64;
|
|
||||||
|
|
||||||
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
|
||||||
macro_rules! case {
|
|
||||||
($($discriminant:ident),*$(,)*) => {
|
|
||||||
/* check that matching on all macro arguments compiles, which means we are not
|
|
||||||
* missing any enum value; if the type definition ever changes this will stop
|
|
||||||
* compiling.
|
|
||||||
*/
|
|
||||||
const fn _assert_exhaustive(val: RegisterOffset) {
|
|
||||||
match val {
|
|
||||||
$(RegisterOffset::$discriminant => (),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match value {
|
|
||||||
$(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)*
|
|
||||||
_ => Err(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod registers {
|
pub mod registers {
|
||||||
//! Device registers exposed as typed structs which are backed by arbitrary
|
//! Device registers exposed as typed structs which are backed by arbitrary
|
||||||
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
//! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc.
|
||||||
|
|
|
@ -3,75 +3,81 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use quote::quote;
|
||||||
use quote::{quote, quote_spanned};
|
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field,
|
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
|
||||||
Fields, Ident, Type, Visibility,
|
DeriveInput, Field, Fields, Ident, Meta, Path, Token, Type, Variant, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompileError(String, Span);
|
mod utils;
|
||||||
|
use utils::MacroError;
|
||||||
|
|
||||||
impl From<CompileError> for proc_macro2::TokenStream {
|
fn get_fields<'a>(
|
||||||
fn from(err: CompileError) -> Self {
|
input: &'a DeriveInput,
|
||||||
let CompileError(msg, span) = err;
|
msg: &str,
|
||||||
quote_spanned! { span => compile_error!(#msg); }
|
) -> Result<&'a Punctuated<Field, Comma>, MacroError> {
|
||||||
|
if let Data::Struct(s) = &input.data {
|
||||||
|
if let Fields::Named(fs) = &s.fields {
|
||||||
|
Ok(&fs.named)
|
||||||
|
} else {
|
||||||
|
Err(MacroError::Message(
|
||||||
|
format!("Named fields required for {}", msg),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(MacroError::Message(
|
||||||
|
format!("Struct required for {}", msg),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> {
|
fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> {
|
||||||
let expected = parse_quote! { #[repr(C)] };
|
let expected = parse_quote! { #[repr(C)] };
|
||||||
|
|
||||||
if input.attrs.iter().any(|attr| attr == &expected) {
|
if input.attrs.iter().any(|attr| attr == &expected) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CompileError(
|
Err(MacroError::Message(
|
||||||
format!("#[repr(C)] required for {}", msg),
|
format!("#[repr(C)] required for {}", msg),
|
||||||
input.ident.span(),
|
input.ident.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(Object)]
|
fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||||
pub fn derive_object(input: TokenStream) -> TokenStream {
|
is_c_repr(&input, "#[derive(Object)]")?;
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let name = input.ident;
|
let name = &input.ident;
|
||||||
|
let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
::qemu_api::assert_field_type!(#name, #parent,
|
||||||
|
::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>);
|
||||||
|
|
||||||
let expanded = quote! {
|
|
||||||
::qemu_api::module_init! {
|
::qemu_api::module_init! {
|
||||||
MODULE_INIT_QOM => unsafe {
|
MODULE_INIT_QOM => unsafe {
|
||||||
::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
|
::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Object)]
|
||||||
|
pub fn derive_object(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let expanded = derive_object_or_error(input).unwrap_or_else(Into::into);
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_fields(input: &DeriveInput) -> Result<&Punctuated<Field, Comma>, CompileError> {
|
|
||||||
if let Data::Struct(s) = &input.data {
|
|
||||||
if let Fields::Named(fs) = &s.fields {
|
|
||||||
Ok(&fs.named)
|
|
||||||
} else {
|
|
||||||
Err(CompileError(
|
|
||||||
"Cannot generate offsets for unnamed fields.".to_string(),
|
|
||||||
input.ident.span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(CompileError(
|
|
||||||
"Cannot generate offsets for union or enum.".to_string(),
|
|
||||||
input.ident.span(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustfmt::skip::macros(quote)]
|
#[rustfmt::skip::macros(quote)]
|
||||||
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, CompileError> {
|
fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||||
is_c_repr(&input, "#[derive(offsets)]")?;
|
is_c_repr(&input, "#[derive(offsets)]")?;
|
||||||
|
|
||||||
let name = &input.ident;
|
let name = &input.ident;
|
||||||
let fields = get_fields(&input)?;
|
let fields = get_fields(&input, "#[derive(offsets)]")?;
|
||||||
let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
|
let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
|
||||||
let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect();
|
let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect();
|
||||||
let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect();
|
let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect();
|
||||||
|
@ -92,3 +98,73 @@ pub fn derive_offsets(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result<Path, MacroError> {
|
||||||
|
let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr"));
|
||||||
|
if let Some(repr) = repr {
|
||||||
|
let nested = repr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
|
||||||
|
for meta in nested {
|
||||||
|
match meta {
|
||||||
|
Meta::Path(path) if path.is_ident("u8") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u16") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u32") => return Ok(path),
|
||||||
|
Meta::Path(path) if path.is_ident("u64") => return Ok(path),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(MacroError::Message(
|
||||||
|
format!("#[repr(u8/u16/u32/u64) required for {}", msg),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_variants(input: &DeriveInput) -> Result<&Punctuated<Variant, Comma>, MacroError> {
|
||||||
|
if let Data::Enum(e) = &input.data {
|
||||||
|
if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) {
|
||||||
|
return Err(MacroError::Message(
|
||||||
|
"Cannot derive TryInto for enum with non-unit variants.".to_string(),
|
||||||
|
v.fields.span(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(&e.variants)
|
||||||
|
} else {
|
||||||
|
Err(MacroError::Message(
|
||||||
|
"Cannot derive TryInto for union or struct.".to_string(),
|
||||||
|
input.ident.span(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip::macros(quote)]
|
||||||
|
fn derive_tryinto_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> {
|
||||||
|
let repr = get_repr_uN(&input, "#[derive(TryInto)]")?;
|
||||||
|
|
||||||
|
let name = &input.ident;
|
||||||
|
let variants = get_variants(&input)?;
|
||||||
|
let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl core::convert::TryFrom<#repr> for #name {
|
||||||
|
type Error = #repr;
|
||||||
|
|
||||||
|
fn try_from(value: #repr) -> Result<Self, Self::Error> {
|
||||||
|
#(const #discriminants: #repr = #name::#discriminants as #repr;)*;
|
||||||
|
match value {
|
||||||
|
#(#discriminants => Ok(Self::#discriminants),)*
|
||||||
|
_ => Err(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(TryInto)]
|
||||||
|
pub fn derive_tryinto(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into);
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
26
rust/qemu-api-macros/src/utils.rs
Normal file
26
rust/qemu-api-macros/src/utils.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Procedural macro utilities.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
use proc_macro2::Span;
|
||||||
|
use quote::quote_spanned;
|
||||||
|
|
||||||
|
pub enum MacroError {
|
||||||
|
Message(String, Span),
|
||||||
|
ParseError(syn::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<syn::Error> for MacroError {
|
||||||
|
fn from(err: syn::Error) -> Self {
|
||||||
|
MacroError::ParseError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MacroError> for proc_macro2::TokenStream {
|
||||||
|
fn from(err: MacroError) -> Self {
|
||||||
|
match err {
|
||||||
|
MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); },
|
||||||
|
MacroError::ParseError(err) => err.into_compile_error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ if rustc.version().version_compare('>=1.77.0')
|
||||||
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
_qemu_api_cfg += ['--cfg', 'has_offset_of']
|
||||||
endif
|
endif
|
||||||
if get_option('debug_mutex')
|
if get_option('debug_mutex')
|
||||||
_qemu_api_cfg += ['--feature', 'debug_cell']
|
_qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
_qemu_api_rs = static_library(
|
_qemu_api_rs = static_library(
|
||||||
|
@ -15,6 +15,7 @@ _qemu_api_rs = static_library(
|
||||||
structured_sources(
|
structured_sources(
|
||||||
[
|
[
|
||||||
'src/lib.rs',
|
'src/lib.rs',
|
||||||
|
'src/assertions.rs',
|
||||||
'src/bindings.rs',
|
'src/bindings.rs',
|
||||||
'src/bitops.rs',
|
'src/bitops.rs',
|
||||||
'src/callbacks.rs',
|
'src/callbacks.rs',
|
||||||
|
|
90
rust/qemu-api/src/assertions.rs
Normal file
90
rust/qemu-api/src/assertions.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2024, Red Hat Inc.
|
||||||
|
// Author(s): Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
//! This module provides macros to check the equality of types and
|
||||||
|
//! the type of `struct` fields. This can be useful to ensure that
|
||||||
|
//! types match the expectations of C code.
|
||||||
|
|
||||||
|
// Based on https://stackoverflow.com/questions/64251852/x/70978292#70978292
|
||||||
|
// (stackoverflow answers are released under MIT license).
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait EqType {
|
||||||
|
type Itself;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EqType for T {
|
||||||
|
type Itself = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that two types are the same.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use qemu_api::assert_same_type;
|
||||||
|
/// # use std::ops::Deref;
|
||||||
|
/// assert_same_type!(u32, u32);
|
||||||
|
/// assert_same_type!(<Box<u32> as Deref>::Target, u32);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Different types will cause a compile failure
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// # use qemu_api::assert_same_type;
|
||||||
|
/// assert_same_type!(&Box<u32>, &u32);
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_same_type {
|
||||||
|
($t1:ty, $t2:ty) => {
|
||||||
|
const _: () = {
|
||||||
|
#[allow(unused)]
|
||||||
|
fn assert_same_type(v: $t1) {
|
||||||
|
fn types_must_be_equal<T, U>(_: T)
|
||||||
|
where
|
||||||
|
T: $crate::assertions::EqType<Itself = U>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
types_must_be_equal::<_, $t2>(v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that a field of a struct has the given type.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use qemu_api::assert_field_type;
|
||||||
|
/// pub struct A {
|
||||||
|
/// field1: u32,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_field_type!(A, field1, u32);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Different types will cause a compile failure
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// # use qemu_api::assert_field_type;
|
||||||
|
/// # pub struct A { field1: u32 }
|
||||||
|
/// assert_field_type!(A, field1, i32);
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_field_type {
|
||||||
|
($t:ty, $i:tt, $ti:ty) => {
|
||||||
|
const _: () = {
|
||||||
|
#[allow(unused)]
|
||||||
|
fn assert_field_type(v: $t) {
|
||||||
|
fn types_must_be_equal<T, U>(_: T)
|
||||||
|
where
|
||||||
|
T: $crate::assertions::EqType<Itself = U>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
types_must_be_equal::<_, $ti>(v.$i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -24,8 +24,7 @@ use crate::{
|
||||||
///
|
///
|
||||||
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
|
/// Interrupts are implemented as a pointer to the interrupt "sink", which has
|
||||||
/// type [`IRQState`]. A device exposes its source as a QOM link property using
|
/// type [`IRQState`]. A device exposes its source as a QOM link property using
|
||||||
/// a function such as
|
/// a function such as [`SysBusDeviceMethods::init_irq`], and
|
||||||
/// [`SysBusDevice::init_irq`](crate::sysbus::SysBusDevice::init_irq), and
|
|
||||||
/// initially leaves the pointer to a NULL value, representing an unconnected
|
/// initially leaves the pointer to a NULL value, representing an unconnected
|
||||||
/// interrupt. To connect it, whoever creates the device fills the pointer with
|
/// interrupt. To connect it, whoever creates the device fills the pointer with
|
||||||
/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
|
/// the sink's `IRQState *`, for example using `sysbus_connect_irq`. Because
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub mod bindings;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod assertions;
|
||||||
pub mod bitops;
|
pub mod bitops;
|
||||||
pub mod c_str;
|
pub mod c_str;
|
||||||
pub mod callbacks;
|
pub mod callbacks;
|
||||||
|
|
|
@ -16,3 +16,5 @@ pub use crate::qom::ObjectMethods;
|
||||||
pub use crate::qom::ObjectType;
|
pub use crate::qom::ObjectType;
|
||||||
|
|
||||||
pub use crate::qom_isa;
|
pub use crate::qom_isa;
|
||||||
|
|
||||||
|
pub use crate::sysbus::SysBusDeviceMethods;
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
|
fmt,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
os::raw::c_void,
|
os::raw::c_void,
|
||||||
};
|
};
|
||||||
|
@ -105,6 +106,52 @@ macro_rules! qom_isa {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is the same as [`ManuallyDrop<T>`](std::mem::ManuallyDrop), though
|
||||||
|
/// it hides the standard methods of `ManuallyDrop`.
|
||||||
|
///
|
||||||
|
/// The first field of an `ObjectType` must be of type `ParentField<T>`.
|
||||||
|
/// (Technically, this is only necessary if there is at least one Rust
|
||||||
|
/// superclass in the hierarchy). This is to ensure that the parent field is
|
||||||
|
/// dropped after the subclass; this drop order is enforced by the C
|
||||||
|
/// `object_deinit` function.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// #[repr(C)]
|
||||||
|
/// #[derive(qemu_api_macros::Object)]
|
||||||
|
/// pub struct MyDevice {
|
||||||
|
/// parent: ParentField<DeviceState>,
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct ParentField<T: ObjectType>(std::mem::ManuallyDrop<T>);
|
||||||
|
|
||||||
|
impl<T: ObjectType> Deref for ParentField<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ObjectType> DerefMut for ParentField<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Display + ObjectType> fmt::Display for ParentField<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
|
unsafe extern "C" fn rust_instance_init<T: ObjectImpl>(obj: *mut Object) {
|
||||||
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
// SAFETY: obj is an instance of T, since rust_instance_init<T>
|
||||||
// is called from QOM core as the instance_init function
|
// is called from QOM core as the instance_init function
|
||||||
|
@ -116,11 +163,7 @@ unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) {
|
||||||
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
|
// SAFETY: obj is an instance of T, since rust_instance_post_init<T>
|
||||||
// is called from QOM core as the instance_post_init function
|
// is called from QOM core as the instance_post_init function
|
||||||
// for class T
|
// for class T
|
||||||
//
|
T::INSTANCE_POST_INIT.unwrap()(unsafe { &*obj.cast::<T>() })
|
||||||
// FIXME: it's not really guaranteed that there are no backpointers to
|
|
||||||
// obj; it's quite possible that they have been created by instance_init().
|
|
||||||
// The receiver should be &self, not &mut self.
|
|
||||||
T::INSTANCE_POST_INIT.unwrap()(unsafe { &mut *obj.cast::<T>() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
|
unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
|
||||||
|
@ -133,6 +176,16 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
|
||||||
T::class_init(unsafe { &mut *klass.cast::<T::Class>() })
|
T::class_init(unsafe { &mut *klass.cast::<T::Class>() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) {
|
||||||
|
// SAFETY: obj is an instance of T, since drop_object<T> is called
|
||||||
|
// from the QOM core function object_deinit() as the instance_finalize
|
||||||
|
// function for class T. Note that while object_deinit() will drop the
|
||||||
|
// superclass field separately after this function returns, `T` must
|
||||||
|
// implement the unsafe trait ObjectType; the safety rules for the
|
||||||
|
// trait mandate that the parent field is manually dropped.
|
||||||
|
unsafe { std::ptr::drop_in_place(obj.cast::<T>()) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait exposed by all structs corresponding to QOM objects.
|
/// Trait exposed by all structs corresponding to QOM objects.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -151,11 +204,16 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>(
|
||||||
///
|
///
|
||||||
/// - the struct must be `#[repr(C)]`;
|
/// - the struct must be `#[repr(C)]`;
|
||||||
///
|
///
|
||||||
/// - the first field of the struct must be of the instance struct corresponding
|
/// - the first field of the struct must be of type
|
||||||
/// to the superclass, which is `ObjectImpl::ParentType`
|
/// [`ParentField<T>`](ParentField), where `T` is the parent type
|
||||||
|
/// [`ObjectImpl::ParentType`]
|
||||||
///
|
///
|
||||||
/// - likewise, the first field of the `Class` must be of the class struct
|
/// - the first field of the `Class` must be of the class struct corresponding
|
||||||
/// corresponding to the superclass, which is `ObjectImpl::ParentType::Class`.
|
/// to the superclass, which is `ObjectImpl::ParentType::Class`. `ParentField`
|
||||||
|
/// is not needed here.
|
||||||
|
///
|
||||||
|
/// In both cases, having a separate class type is not necessary if the subclass
|
||||||
|
/// does not add any field.
|
||||||
pub unsafe trait ObjectType: Sized {
|
pub unsafe trait ObjectType: Sized {
|
||||||
/// The QOM class object corresponding to this struct. This is used
|
/// The QOM class object corresponding to this struct. This is used
|
||||||
/// to automatically generate a `class_init` method.
|
/// to automatically generate a `class_init` method.
|
||||||
|
@ -384,13 +442,12 @@ 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 + ClassInitImpl<Self::Class> {
|
||||||
/// The parent of the type. This should match the first field of
|
/// The parent of the type. This should match the first field of the
|
||||||
/// the struct that implements `ObjectImpl`:
|
/// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper.
|
||||||
type ParentType: ObjectType;
|
type ParentType: ObjectType;
|
||||||
|
|
||||||
/// Whether the object can be instantiated
|
/// Whether the object can be instantiated
|
||||||
const ABSTRACT: bool = false;
|
const ABSTRACT: bool = false;
|
||||||
const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
|
|
||||||
|
|
||||||
/// Function that is called to initialize an object. The parent class will
|
/// Function that is called to initialize an object. The parent class will
|
||||||
/// have already been initialized so the type is only responsible for
|
/// have already been initialized so the type is only responsible for
|
||||||
|
@ -402,7 +459,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
|
||||||
|
|
||||||
/// Function that is called to finish initialization of an object, once
|
/// Function that is called to finish initialization of an object, once
|
||||||
/// `INSTANCE_INIT` functions have been called.
|
/// `INSTANCE_INIT` functions have been called.
|
||||||
const INSTANCE_POST_INIT: Option<fn(&mut Self)> = None;
|
const INSTANCE_POST_INIT: Option<fn(&Self)> = None;
|
||||||
|
|
||||||
/// Called on descendent classes after all parent class initialization
|
/// Called on descendent classes after all parent class initialization
|
||||||
/// has occurred, but before the class itself is initialized. This
|
/// has occurred, but before the class itself is initialized. This
|
||||||
|
@ -426,7 +483,7 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> {
|
||||||
None => None,
|
None => None,
|
||||||
Some(_) => Some(rust_instance_post_init::<Self>),
|
Some(_) => Some(rust_instance_post_init::<Self>),
|
||||||
},
|
},
|
||||||
instance_finalize: Self::INSTANCE_FINALIZE,
|
instance_finalize: Some(drop_object::<Self>),
|
||||||
abstract_: Self::ABSTRACT,
|
abstract_: Self::ABSTRACT,
|
||||||
class_size: core::mem::size_of::<Self::Class>(),
|
class_size: core::mem::size_of::<Self::Class>(),
|
||||||
class_init: Some(rust_class_init::<Self>),
|
class_init: Some(rust_class_init::<Self>),
|
||||||
|
|
|
@ -32,20 +32,33 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysBusDevice {
|
/// Trait for methods of [`SysBusDevice`] and its subclasses.
|
||||||
/// Return `self` cast to a mutable pointer, for use in calls to C code.
|
pub trait SysBusDeviceMethods: ObjectDeref
|
||||||
const fn as_mut_ptr(&self) -> *mut SysBusDevice {
|
where
|
||||||
addr_of!(*self) as *mut _
|
Self::Target: IsA<SysBusDevice>,
|
||||||
|
{
|
||||||
|
/// Expose a memory region to the board so that it can give it an address
|
||||||
|
/// in guest memory. Note that the ordering of calls to `init_mmio` is
|
||||||
|
/// important, since whoever creates the sysbus device will refer to the
|
||||||
|
/// region with a number that corresponds to the order of calls to
|
||||||
|
/// `init_mmio`.
|
||||||
|
fn init_mmio(&self, iomem: &bindings::MemoryRegion) {
|
||||||
|
assert!(bql_locked());
|
||||||
|
unsafe {
|
||||||
|
bindings::sysbus_init_mmio(self.as_mut_ptr(), addr_of!(*iomem) as *mut _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expose an interrupt source outside the device as a qdev GPIO output.
|
/// Expose an interrupt source outside the device as a qdev GPIO output.
|
||||||
/// Note that the ordering of calls to `init_irq` is important, since
|
/// Note that the ordering of calls to `init_irq` is important, since
|
||||||
/// whoever creates the sysbus device will refer to the interrupts with
|
/// whoever creates the sysbus device will refer to the interrupts with
|
||||||
/// a number that corresponds to the order of calls to `init_irq`.
|
/// a number that corresponds to the order of calls to `init_irq`.
|
||||||
pub fn init_irq(&self, irq: &InterruptSource) {
|
fn init_irq(&self, irq: &InterruptSource) {
|
||||||
assert!(bql_locked());
|
assert!(bql_locked());
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
|
bindings::sysbus_init_irq(self.as_mut_ptr(), irq.as_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use qemu_api::{
|
||||||
declare_properties, define_property,
|
declare_properties, define_property,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{DeviceImpl, DeviceState, Property},
|
qdev::{DeviceImpl, DeviceState, Property},
|
||||||
qom::ObjectImpl,
|
qom::{ObjectImpl, ParentField},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
zeroable::Zeroable,
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ pub static VMSTATE: VMStateDescription = VMStateDescription {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(qemu_api_macros::Object)]
|
#[derive(qemu_api_macros::Object)]
|
||||||
pub struct DummyState {
|
pub struct DummyState {
|
||||||
parent: DeviceState,
|
parent: ParentField<DeviceState>,
|
||||||
migrate_clock: bool,
|
migrate_clock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,27 @@
|
||||||
# This work is licensed under the terms of the GNU GPLv2 or later.
|
# This work is licensed under the terms of the GNU GPLv2 or later.
|
||||||
# See the COPYING file in the top-level directory.
|
# See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
function subproject_dir() {
|
||||||
|
if test ! -f "subprojects/$1.wrap"; then
|
||||||
|
error "scripts/archive-source.sh should only process wrap subprojects"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print the directory key of the wrap file, defaulting to the
|
||||||
|
# subproject name. The wrap file is in ini format and should
|
||||||
|
# have a single section only. There should be only one section
|
||||||
|
# named "[wrap-*]", which helps keeping the script simple.
|
||||||
|
local dir
|
||||||
|
dir=$(sed -n \
|
||||||
|
-e '/^\[wrap-[a-z][a-z]*\]$/,/^\[/{' \
|
||||||
|
-e '/^directory *= */!b' \
|
||||||
|
-e 's///p' \
|
||||||
|
-e 'q' \
|
||||||
|
-e '}' \
|
||||||
|
"subprojects/$1.wrap")
|
||||||
|
|
||||||
|
echo "${dir:-$1}"
|
||||||
|
}
|
||||||
|
|
||||||
if [ $# -ne 2 ]; then
|
if [ $# -ne 2 ]; then
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo " $0 gitrepo version"
|
echo " $0 gitrepo version"
|
||||||
|
@ -51,5 +72,13 @@ meson subprojects download $SUBPROJECTS
|
||||||
CryptoPkg/Library/OpensslLib/openssl \
|
CryptoPkg/Library/OpensslLib/openssl \
|
||||||
MdeModulePkg/Library/BrotliCustomDecompressLib/brotli)
|
MdeModulePkg/Library/BrotliCustomDecompressLib/brotli)
|
||||||
popd
|
popd
|
||||||
tar --exclude=.git -cJf ${destination}.tar.xz ${destination}
|
|
||||||
|
exclude=(--exclude=.git)
|
||||||
|
# include the tarballs in subprojects/packagecache but not their expansion
|
||||||
|
for sp in $SUBPROJECTS; do
|
||||||
|
if grep -xqF "[wrap-file]" subprojects/$sp.wrap; then
|
||||||
|
exclude+=(--exclude=subprojects/"$(subproject_dir $sp)")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
tar "${exclude[@]}" -cJf ${destination}.tar.xz ${destination}
|
||||||
rm -rf ${destination}
|
rm -rf ${destination}
|
||||||
|
|
|
@ -215,6 +215,8 @@ def main() -> None:
|
||||||
|
|
||||||
if rustc_version >= (1, 80):
|
if rustc_version >= (1, 80):
|
||||||
if args.lints:
|
if args.lints:
|
||||||
|
print("--check-cfg")
|
||||||
|
print("cfg(test)")
|
||||||
for cfg in sorted(cargo_toml.check_cfg):
|
for cfg in sorted(cargo_toml.check_cfg):
|
||||||
print("--check-cfg")
|
print("--check-cfg")
|
||||||
print(cfg)
|
print(cfg)
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = arbitrary-int-1.2.7.tar.gz
|
||||||
source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d
|
source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = arbitrary-int-1-rs
|
patch_directory = arbitrary-int-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = bilge-0.2.0.tar.gz
|
||||||
source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57
|
source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = bilge-0.2-rs
|
patch_directory = bilge-0.2-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -6,3 +6,6 @@ source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = bilge-impl-0.2-rs
|
patch_directory = bilge-impl-0.2-rs
|
||||||
diff_files = bilge-impl-1.63.0.patch
|
diff_files = bilge-impl-1.63.0.patch
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = either-1.12.0.tar.gz
|
||||||
source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b
|
source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = either-1-rs
|
patch_directory = either-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = itertools-0.11.0.tar.gz
|
||||||
source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57
|
source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = itertools-0.11-rs
|
patch_directory = itertools-0.11-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -9,6 +9,7 @@ _arbitrary_int_rs = static_library(
|
||||||
files('src/lib.rs'),
|
files('src/lib.rs'),
|
||||||
gnu_symbol_visibility: 'hidden',
|
gnu_symbol_visibility: 'hidden',
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
|
rust_args: ['--cap-lints', 'allow'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,6 +17,7 @@ lib = static_library(
|
||||||
'src/lib.rs',
|
'src/lib.rs',
|
||||||
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: ['--cap-lints', 'allow'],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
arbitrary_int_dep,
|
arbitrary_int_dep,
|
||||||
bilge_impl_dep,
|
bilge_impl_dep,
|
||||||
|
|
|
@ -25,6 +25,7 @@ _bilge_impl_rs = rust.proc_macro(
|
||||||
files('src/lib.rs'),
|
files('src/lib.rs'),
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
rust_args: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'use_fallback',
|
'--cfg', 'use_fallback',
|
||||||
'--cfg', 'feature="syn-error"',
|
'--cfg', 'feature="syn-error"',
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
|
|
|
@ -11,6 +11,7 @@ _either_rs = static_library(
|
||||||
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'feature="use_std"',
|
'--cfg', 'feature="use_std"',
|
||||||
'--cfg', 'feature="use_alloc"',
|
'--cfg', 'feature="use_alloc"',
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,6 +15,7 @@ _itertools_rs = static_library(
|
||||||
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'feature="use_std"',
|
'--cfg', 'feature="use_std"',
|
||||||
'--cfg', 'feature="use_alloc"',
|
'--cfg', 'feature="use_alloc"',
|
||||||
],
|
],
|
||||||
|
|
|
@ -20,6 +20,7 @@ _proc_macro_error_rs = static_library(
|
||||||
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
||||||
rust_abi: 'rust',
|
rust_abi: 'rust',
|
||||||
rust_args: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'use_fallback',
|
'--cfg', 'use_fallback',
|
||||||
'--cfg', 'feature="syn-error"',
|
'--cfg', 'feature="syn-error"',
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
|
|
|
@ -16,6 +16,7 @@ _proc_macro_error_attr_rs = rust.proc_macro(
|
||||||
files('src/lib.rs'),
|
files('src/lib.rs'),
|
||||||
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
override_options: ['rust_std=2018', 'build.rust_std=2018'],
|
||||||
rust_args: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'use_fallback',
|
'--cfg', 'use_fallback',
|
||||||
'--cfg', 'feature="syn-error"',
|
'--cfg', 'feature="syn-error"',
|
||||||
'--cfg', 'feature="proc-macro"'
|
'--cfg', 'feature="proc-macro"'
|
||||||
|
|
|
@ -15,6 +15,7 @@ _proc_macro2_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: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
'--cfg', 'no_literal_byte_character',
|
'--cfg', 'no_literal_byte_character',
|
||||||
'--cfg', 'no_literal_c_string',
|
'--cfg', 'no_literal_c_string',
|
||||||
|
|
|
@ -15,6 +15,7 @@ _quote_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: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'feature="proc-macro"',
|
'--cfg', 'feature="proc-macro"',
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
|
|
@ -19,6 +19,7 @@ _syn_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: [
|
rust_args: [
|
||||||
|
'--cap-lints', 'allow',
|
||||||
'--cfg', 'feature="full"',
|
'--cfg', 'feature="full"',
|
||||||
'--cfg', 'feature="derive"',
|
'--cfg', 'feature="derive"',
|
||||||
'--cfg', 'feature="parsing"',
|
'--cfg', 'feature="parsing"',
|
||||||
|
|
|
@ -10,6 +10,7 @@ _unicode_ident_rs = static_library(
|
||||||
gnu_symbol_visibility: 'hidden',
|
gnu_symbol_visibility: 'hidden',
|
||||||
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: ['--cap-lints', 'allow'],
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
native: true,
|
native: true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = proc-macro-error-1.0.4.tar.gz
|
||||||
source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c
|
source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = proc-macro-error-1-rs
|
patch_directory = proc-macro-error-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = proc-macro-error-attr-1.0.4.tar.gz
|
||||||
source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869
|
source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = proc-macro-error-attr-1-rs
|
patch_directory = proc-macro-error-attr-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = proc-macro2-1.0.84.0.tar.gz
|
||||||
source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6
|
source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = proc-macro2-1-rs
|
patch_directory = proc-macro2-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = quote-1.0.36.0.tar.gz
|
||||||
source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7
|
source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = quote-1-rs
|
patch_directory = quote-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = syn-2.0.66.0.tar.gz
|
||||||
source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5
|
source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = syn-2-rs
|
patch_directory = syn-2-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -5,3 +5,6 @@ source_filename = unicode-ident-1.0.12.tar.gz
|
||||||
source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b
|
source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b
|
||||||
#method = cargo
|
#method = cargo
|
||||||
patch_directory = unicode-ident-1-rs
|
patch_directory = unicode-ident-1-rs
|
||||||
|
|
||||||
|
# bump this version number on every change to meson.build or the patches:
|
||||||
|
# v2
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
project('unicode-ident-1-rs', 'rust',
|
|
||||||
version: '1.0.12',
|
|
||||||
license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016',
|
|
||||||
default_options: [])
|
|
||||||
|
|
||||||
_unicode_ident_rs = static_library(
|
|
||||||
'unicode_ident',
|
|
||||||
files('src/lib.rs'),
|
|
||||||
gnu_symbol_visibility: 'hidden',
|
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
|
||||||
rust_abi: 'rust',
|
|
||||||
dependencies: [],
|
|
||||||
native: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
unicode_ident_dep = declare_dependency(
|
|
||||||
link_with: _unicode_ident_rs,
|
|
||||||
)
|
|
||||||
|
|
||||||
meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true)
|
|
|
@ -687,7 +687,6 @@ void qemu_init_vcpu(CPUState *cpu)
|
||||||
{
|
{
|
||||||
MachineState *ms = MACHINE(qdev_get_machine());
|
MachineState *ms = MACHINE(qdev_get_machine());
|
||||||
|
|
||||||
cpu->nr_cores = machine_topo_get_cores_per_socket(ms);
|
|
||||||
cpu->nr_threads = ms->smp.threads;
|
cpu->nr_threads = ms->smp.threads;
|
||||||
cpu->stopped = true;
|
cpu->stopped = true;
|
||||||
cpu->random_seed = qemu_guest_random_seed_thread_part1();
|
cpu->random_seed = qemu_guest_random_seed_thread_part1();
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct X86ConfidentialGuestClass {
|
||||||
/**
|
/**
|
||||||
* x86_confidential_guest_kvm_type:
|
* x86_confidential_guest_kvm_type:
|
||||||
*
|
*
|
||||||
* Calls #X86ConfidentialGuestClass.unplug callback of @plug_handler.
|
* Calls #X86ConfidentialGuestClass.kvm_type() callback.
|
||||||
*/
|
*/
|
||||||
static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg)
|
static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -309,3 +309,14 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v,
|
||||||
errp);
|
errp);
|
||||||
qapi_free_GuestPanicInformation(panic_info);
|
qapi_free_GuestPanicInformation(panic_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu)
|
||||||
|
{
|
||||||
|
CPUX86State *env = &cpu->env;
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
val = x86_threads_per_pkg(&env->topo_info); /* thread count, bits 15..0 */
|
||||||
|
val |= x86_cores_per_pkg(&env->topo_info) << 16; /* core count, bits 31..16 */
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
|
@ -312,13 +312,11 @@ static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info,
|
||||||
case CPU_TOPOLOGY_LEVEL_CORE:
|
case CPU_TOPOLOGY_LEVEL_CORE:
|
||||||
return topo_info->threads_per_core;
|
return topo_info->threads_per_core;
|
||||||
case CPU_TOPOLOGY_LEVEL_MODULE:
|
case CPU_TOPOLOGY_LEVEL_MODULE:
|
||||||
return topo_info->threads_per_core * topo_info->cores_per_module;
|
return x86_threads_per_module(topo_info);
|
||||||
case CPU_TOPOLOGY_LEVEL_DIE:
|
case CPU_TOPOLOGY_LEVEL_DIE:
|
||||||
return topo_info->threads_per_core * topo_info->cores_per_module *
|
return x86_threads_per_die(topo_info);
|
||||||
topo_info->modules_per_die;
|
|
||||||
case CPU_TOPOLOGY_LEVEL_SOCKET:
|
case CPU_TOPOLOGY_LEVEL_SOCKET:
|
||||||
return topo_info->threads_per_core * topo_info->cores_per_module *
|
return x86_threads_per_pkg(topo_info);
|
||||||
topo_info->modules_per_die * topo_info->dies_per_pkg;
|
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
@ -6498,18 +6496,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
CPUState *cs = env_cpu(env);
|
CPUState *cs = env_cpu(env);
|
||||||
uint32_t limit;
|
uint32_t limit;
|
||||||
uint32_t signature[3];
|
uint32_t signature[3];
|
||||||
X86CPUTopoInfo topo_info;
|
X86CPUTopoInfo *topo_info = &env->topo_info;
|
||||||
uint32_t cores_per_pkg;
|
|
||||||
uint32_t threads_per_pkg;
|
uint32_t threads_per_pkg;
|
||||||
|
|
||||||
topo_info.dies_per_pkg = env->nr_dies;
|
threads_per_pkg = x86_threads_per_pkg(topo_info);
|
||||||
topo_info.modules_per_die = env->nr_modules;
|
|
||||||
topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules;
|
|
||||||
topo_info.threads_per_core = cs->nr_threads;
|
|
||||||
|
|
||||||
cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die *
|
|
||||||
topo_info.dies_per_pkg;
|
|
||||||
threads_per_pkg = cores_per_pkg * topo_info.threads_per_core;
|
|
||||||
|
|
||||||
/* Calculate & apply limits for different index ranges */
|
/* Calculate & apply limits for different index ranges */
|
||||||
if (index >= 0xC0000000) {
|
if (index >= 0xC0000000) {
|
||||||
|
@ -6548,7 +6538,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
*edx = env->features[FEAT_1_EDX];
|
*edx = env->features[FEAT_1_EDX];
|
||||||
if (threads_per_pkg > 1) {
|
if (threads_per_pkg > 1) {
|
||||||
*ebx |= threads_per_pkg << 16;
|
*ebx |= threads_per_pkg << 16;
|
||||||
*edx |= CPUID_HT;
|
|
||||||
}
|
}
|
||||||
if (!cpu->enable_pmu) {
|
if (!cpu->enable_pmu) {
|
||||||
*ecx &= ~CPUID_EXT_PDCM;
|
*ecx &= ~CPUID_EXT_PDCM;
|
||||||
|
@ -6586,12 +6575,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14);
|
int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14);
|
||||||
|
|
||||||
*eax &= ~0xFC000000;
|
*eax &= ~0xFC000000;
|
||||||
*eax |= max_core_ids_in_package(&topo_info) << 26;
|
*eax |= max_core_ids_in_package(topo_info) << 26;
|
||||||
if (host_vcpus_per_cache > threads_per_pkg) {
|
if (host_vcpus_per_cache > threads_per_pkg) {
|
||||||
*eax &= ~0x3FFC000;
|
*eax &= ~0x3FFC000;
|
||||||
|
|
||||||
/* Share the cache at package level. */
|
/* Share the cache at package level. */
|
||||||
*eax |= max_thread_ids_for_cache(&topo_info,
|
*eax |= max_thread_ids_for_cache(topo_info,
|
||||||
CPU_TOPOLOGY_LEVEL_SOCKET) << 14;
|
CPU_TOPOLOGY_LEVEL_SOCKET) << 14;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6603,7 +6592,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
switch (count) {
|
switch (count) {
|
||||||
case 0: /* L1 dcache info */
|
case 0: /* L1 dcache info */
|
||||||
encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache,
|
encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache,
|
||||||
&topo_info,
|
topo_info,
|
||||||
eax, ebx, ecx, edx);
|
eax, ebx, ecx, edx);
|
||||||
if (!cpu->l1_cache_per_core) {
|
if (!cpu->l1_cache_per_core) {
|
||||||
*eax &= ~MAKE_64BIT_MASK(14, 12);
|
*eax &= ~MAKE_64BIT_MASK(14, 12);
|
||||||
|
@ -6611,7 +6600,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
break;
|
break;
|
||||||
case 1: /* L1 icache info */
|
case 1: /* L1 icache info */
|
||||||
encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache,
|
encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache,
|
||||||
&topo_info,
|
topo_info,
|
||||||
eax, ebx, ecx, edx);
|
eax, ebx, ecx, edx);
|
||||||
if (!cpu->l1_cache_per_core) {
|
if (!cpu->l1_cache_per_core) {
|
||||||
*eax &= ~MAKE_64BIT_MASK(14, 12);
|
*eax &= ~MAKE_64BIT_MASK(14, 12);
|
||||||
|
@ -6619,13 +6608,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
break;
|
break;
|
||||||
case 2: /* L2 cache info */
|
case 2: /* L2 cache info */
|
||||||
encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache,
|
encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache,
|
||||||
&topo_info,
|
topo_info,
|
||||||
eax, ebx, ecx, edx);
|
eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
case 3: /* L3 cache info */
|
case 3: /* L3 cache info */
|
||||||
if (cpu->enable_l3_cache) {
|
if (cpu->enable_l3_cache) {
|
||||||
encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache,
|
encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache,
|
||||||
&topo_info,
|
topo_info,
|
||||||
eax, ebx, ecx, edx);
|
eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -6708,12 +6697,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
|
|
||||||
switch (count) {
|
switch (count) {
|
||||||
case 0:
|
case 0:
|
||||||
*eax = apicid_core_offset(&topo_info);
|
*eax = apicid_core_offset(topo_info);
|
||||||
*ebx = topo_info.threads_per_core;
|
*ebx = topo_info->threads_per_core;
|
||||||
*ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8;
|
*ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
*eax = apicid_pkg_offset(&topo_info);
|
*eax = apicid_pkg_offset(topo_info);
|
||||||
*ebx = threads_per_pkg;
|
*ebx = threads_per_pkg;
|
||||||
*ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8;
|
*ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8;
|
||||||
break;
|
break;
|
||||||
|
@ -6739,7 +6728,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx);
|
encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
case 0xD: {
|
case 0xD: {
|
||||||
/* Processor Extended State */
|
/* Processor Extended State */
|
||||||
|
@ -6964,17 +6953,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
*ecx = env->features[FEAT_8000_0001_ECX];
|
*ecx = env->features[FEAT_8000_0001_ECX];
|
||||||
*edx = env->features[FEAT_8000_0001_EDX];
|
*edx = env->features[FEAT_8000_0001_EDX];
|
||||||
|
|
||||||
/* The Linux kernel checks for the CMPLegacy bit and
|
|
||||||
* discards multiple thread information if it is set.
|
|
||||||
* So don't set it here for Intel to make Linux guests happy.
|
|
||||||
*/
|
|
||||||
if (threads_per_pkg > 1) {
|
|
||||||
if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 ||
|
|
||||||
env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 ||
|
|
||||||
env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) {
|
|
||||||
*ecx |= 1 << 1; /* CmpLegacy bit */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 &&
|
if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 &&
|
||||||
!(env->hflags & HF_LMA_MASK)) {
|
!(env->hflags & HF_LMA_MASK)) {
|
||||||
*edx &= ~CPUID_EXT2_SYSCALL;
|
*edx &= ~CPUID_EXT2_SYSCALL;
|
||||||
|
@ -7042,7 +7020,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
* thread ID within a package".
|
* thread ID within a package".
|
||||||
* Bits 7:0 is "The number of threads in the package is NC+1"
|
* Bits 7:0 is "The number of threads in the package is NC+1"
|
||||||
*/
|
*/
|
||||||
*ecx = (apicid_pkg_offset(&topo_info) << 12) |
|
*ecx = (apicid_pkg_offset(topo_info) << 12) |
|
||||||
(threads_per_pkg - 1);
|
(threads_per_pkg - 1);
|
||||||
} else {
|
} else {
|
||||||
*ecx = 0;
|
*ecx = 0;
|
||||||
|
@ -7071,19 +7049,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
switch (count) {
|
switch (count) {
|
||||||
case 0: /* L1 dcache info */
|
case 0: /* L1 dcache info */
|
||||||
encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache,
|
encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache,
|
||||||
&topo_info, eax, ebx, ecx, edx);
|
topo_info, eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
case 1: /* L1 icache info */
|
case 1: /* L1 icache info */
|
||||||
encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache,
|
encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache,
|
||||||
&topo_info, eax, ebx, ecx, edx);
|
topo_info, eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
case 2: /* L2 cache info */
|
case 2: /* L2 cache info */
|
||||||
encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache,
|
encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache,
|
||||||
&topo_info, eax, ebx, ecx, edx);
|
topo_info, eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
case 3: /* L3 cache info */
|
case 3: /* L3 cache info */
|
||||||
encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache,
|
encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache,
|
||||||
&topo_info, eax, ebx, ecx, edx);
|
topo_info, eax, ebx, ecx, edx);
|
||||||
break;
|
break;
|
||||||
default: /* end of info */
|
default: /* end of info */
|
||||||
*eax = *ebx = *ecx = *edx = 0;
|
*eax = *ebx = *ecx = *edx = 0;
|
||||||
|
@ -7095,7 +7073,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
||||||
break;
|
break;
|
||||||
case 0x8000001E:
|
case 0x8000001E:
|
||||||
if (cpu->core_id <= 255) {
|
if (cpu->core_id <= 255) {
|
||||||
encode_topo_cpuid8000001e(cpu, &topo_info, eax, ebx, ecx, edx);
|
encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx);
|
||||||
} else {
|
} else {
|
||||||
*eax = 0;
|
*eax = 0;
|
||||||
*ebx = 0;
|
*ebx = 0;
|
||||||
|
@ -7539,6 +7517,19 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (x86_threads_per_pkg(&env->topo_info) > 1) {
|
||||||
|
env->features[FEAT_1_EDX] |= CPUID_HT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Linux kernel checks for the CMPLegacy bit and
|
||||||
|
* discards multiple thread information if it is set.
|
||||||
|
* So don't set it here for Intel to make Linux guests happy.
|
||||||
|
*/
|
||||||
|
if (!IS_INTEL_CPU(env)) {
|
||||||
|
env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) {
|
for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) {
|
||||||
FeatureDep *d = &feature_dependencies[i];
|
FeatureDep *d = &feature_dependencies[i];
|
||||||
if (!(env->features[d->from.index] & d->from.mask)) {
|
if (!(env->features[d->from.index] & d->from.mask)) {
|
||||||
|
@ -7719,8 +7710,10 @@ static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose)
|
||||||
env->avx10_version = version;
|
env->avx10_version = version;
|
||||||
have_filtered_features = true;
|
have_filtered_features = true;
|
||||||
}
|
}
|
||||||
} else if (env->avx10_version && prefix) {
|
} else if (env->avx10_version) {
|
||||||
warn_report("%s: avx10.%d.", prefix, env->avx10_version);
|
if (prefix) {
|
||||||
|
warn_report("%s: avx10.%d.", prefix, env->avx10_version);
|
||||||
|
}
|
||||||
have_filtered_features = true;
|
have_filtered_features = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7891,6 +7884,21 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
*/
|
*/
|
||||||
cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE;
|
cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU
|
||||||
|
* fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX
|
||||||
|
* based on inputs (sockets,cores,threads), it is still better to give
|
||||||
|
* users a warning.
|
||||||
|
*/
|
||||||
|
if (IS_AMD_CPU(env) &&
|
||||||
|
!(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) &&
|
||||||
|
env->topo_info.threads_per_core > 1) {
|
||||||
|
warn_report_once("This family of AMD CPU doesn't support "
|
||||||
|
"hyperthreading(%d). Please configure -smp "
|
||||||
|
"options properly or try enabling topoext "
|
||||||
|
"feature.", env->topo_info.threads_per_core);
|
||||||
|
}
|
||||||
|
|
||||||
/* For 64bit systems think about the number of physical bits to present.
|
/* For 64bit systems think about the number of physical bits to present.
|
||||||
* ideally this should be the same as the host; anything other than matching
|
* ideally this should be the same as the host; anything other than matching
|
||||||
* the host can cause incorrect guest behaviour.
|
* the host can cause incorrect guest behaviour.
|
||||||
|
@ -7995,24 +8003,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
x86_cpu_gdb_init(cs);
|
x86_cpu_gdb_init(cs);
|
||||||
qemu_init_vcpu(cs);
|
qemu_init_vcpu(cs);
|
||||||
|
|
||||||
/*
|
|
||||||
* Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU
|
|
||||||
* fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX
|
|
||||||
* based on inputs (sockets,cores,threads), it is still better to give
|
|
||||||
* users a warning.
|
|
||||||
*
|
|
||||||
* NOTE: the following code has to follow qemu_init_vcpu(). Otherwise
|
|
||||||
* cs->nr_threads hasn't be populated yet and the checking is incorrect.
|
|
||||||
*/
|
|
||||||
if (IS_AMD_CPU(env) &&
|
|
||||||
!(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) &&
|
|
||||||
cs->nr_threads > 1) {
|
|
||||||
warn_report_once("This family of AMD CPU doesn't support "
|
|
||||||
"hyperthreading(%d). Please configure -smp "
|
|
||||||
"options properly or try enabling topoext "
|
|
||||||
"feature.", cs->nr_threads);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
x86_cpu_apic_realize(cpu, &local_err);
|
x86_cpu_apic_realize(cpu, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
|
@ -8171,8 +8161,7 @@ static void x86_cpu_init_default_topo(X86CPU *cpu)
|
||||||
{
|
{
|
||||||
CPUX86State *env = &cpu->env;
|
CPUX86State *env = &cpu->env;
|
||||||
|
|
||||||
env->nr_modules = 1;
|
env->topo_info = (X86CPUTopoInfo) {1, 1, 1, 1};
|
||||||
env->nr_dies = 1;
|
|
||||||
|
|
||||||
/* thread, core and socket levels are set by default. */
|
/* thread, core and socket levels are set by default. */
|
||||||
set_bit(CPU_TOPOLOGY_LEVEL_THREAD, env->avail_cpu_topo);
|
set_bit(CPU_TOPOLOGY_LEVEL_THREAD, env->avail_cpu_topo);
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "qapi/qapi-types-common.h"
|
#include "qapi/qapi-types-common.h"
|
||||||
#include "qemu/cpu-float.h"
|
#include "qemu/cpu-float.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "standard-headers/asm-x86/kvm_para.h"
|
||||||
|
|
||||||
#define XEN_NR_VIRQS 24
|
#define XEN_NR_VIRQS 24
|
||||||
|
|
||||||
|
@ -1010,6 +1011,28 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
|
||||||
#define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0)
|
#define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0)
|
||||||
#define CPUID_8000_0007_EBX_SUCCOR (1U << 1)
|
#define CPUID_8000_0007_EBX_SUCCOR (1U << 1)
|
||||||
|
|
||||||
|
/* (Old) KVM paravirtualized clocksource */
|
||||||
|
#define CPUID_KVM_CLOCK (1U << KVM_FEATURE_CLOCKSOURCE)
|
||||||
|
/* (New) KVM specific paravirtualized clocksource */
|
||||||
|
#define CPUID_KVM_CLOCK2 (1U << KVM_FEATURE_CLOCKSOURCE2)
|
||||||
|
/* KVM asynchronous page fault */
|
||||||
|
#define CPUID_KVM_ASYNCPF (1U << KVM_FEATURE_ASYNC_PF)
|
||||||
|
/* KVM stolen (when guest vCPU is not running) time accounting */
|
||||||
|
#define CPUID_KVM_STEAL_TIME (1U << KVM_FEATURE_STEAL_TIME)
|
||||||
|
/* KVM paravirtualized end-of-interrupt signaling */
|
||||||
|
#define CPUID_KVM_PV_EOI (1U << KVM_FEATURE_PV_EOI)
|
||||||
|
/* KVM paravirtualized spinlocks support */
|
||||||
|
#define CPUID_KVM_PV_UNHALT (1U << KVM_FEATURE_PV_UNHALT)
|
||||||
|
/* KVM host-side polling on HLT control from the guest */
|
||||||
|
#define CPUID_KVM_POLL_CONTROL (1U << KVM_FEATURE_POLL_CONTROL)
|
||||||
|
/* KVM interrupt based asynchronous page fault*/
|
||||||
|
#define CPUID_KVM_ASYNCPF_INT (1U << KVM_FEATURE_ASYNC_PF_INT)
|
||||||
|
/* KVM 'Extended Destination ID' support for external interrupts */
|
||||||
|
#define CPUID_KVM_MSI_EXT_DEST_ID (1U << KVM_FEATURE_MSI_EXT_DEST_ID)
|
||||||
|
|
||||||
|
/* Hint to KVM that vCPUs expect never preempted for an unlimited time */
|
||||||
|
#define CPUID_KVM_HINTS_REALTIME (1U << KVM_HINTS_REALTIME)
|
||||||
|
|
||||||
/* CLZERO instruction */
|
/* CLZERO instruction */
|
||||||
#define CPUID_8000_0008_EBX_CLZERO (1U << 0)
|
#define CPUID_8000_0008_EBX_CLZERO (1U << 0)
|
||||||
/* Always save/restore FP error pointers */
|
/* Always save/restore FP error pointers */
|
||||||
|
@ -2045,11 +2068,7 @@ typedef struct CPUArchState {
|
||||||
|
|
||||||
TPRAccess tpr_access_type;
|
TPRAccess tpr_access_type;
|
||||||
|
|
||||||
/* Number of dies within this CPU package. */
|
X86CPUTopoInfo topo_info;
|
||||||
unsigned nr_dies;
|
|
||||||
|
|
||||||
/* Number of modules within one die. */
|
|
||||||
unsigned nr_modules;
|
|
||||||
|
|
||||||
/* Bitmap of available CPU topology levels for this CPU. */
|
/* Bitmap of available CPU topology levels for this CPU. */
|
||||||
DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX);
|
DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX);
|
||||||
|
@ -2390,6 +2409,8 @@ static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu,
|
||||||
cs->halted = 0;
|
cs->halted = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t cpu_x86_get_msr_core_thread_count(X86CPU *cpu);
|
||||||
|
|
||||||
int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
|
int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
|
||||||
target_ulong *base, unsigned int *limit,
|
target_ulong *base, unsigned int *limit,
|
||||||
unsigned int *flags);
|
unsigned int *flags);
|
||||||
|
|
|
@ -765,8 +765,7 @@ void simulate_rdmsr(CPUX86State *env)
|
||||||
val = env->mtrr_deftype;
|
val = env->mtrr_deftype;
|
||||||
break;
|
break;
|
||||||
case MSR_CORE_THREAD_COUNT:
|
case MSR_CORE_THREAD_COUNT:
|
||||||
val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */
|
val = cpu_x86_get_msr_core_thread_count(cpu);
|
||||||
val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */
|
/* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */
|
||||||
|
|
|
@ -95,9 +95,6 @@
|
||||||
#define KVM_APIC_BUS_CYCLE_NS 1
|
#define KVM_APIC_BUS_CYCLE_NS 1
|
||||||
#define KVM_APIC_BUS_FREQUENCY (1000000000ULL / KVM_APIC_BUS_CYCLE_NS)
|
#define KVM_APIC_BUS_FREQUENCY (1000000000ULL / KVM_APIC_BUS_CYCLE_NS)
|
||||||
|
|
||||||
#define MSR_KVM_WALL_CLOCK 0x11
|
|
||||||
#define MSR_KVM_SYSTEM_TIME 0x12
|
|
||||||
|
|
||||||
/* A 4096-byte buffer can hold the 8-byte kvm_msrs header, plus
|
/* A 4096-byte buffer can hold the 8-byte kvm_msrs header, plus
|
||||||
* 255 kvm_msr_entry structs */
|
* 255 kvm_msr_entry structs */
|
||||||
#define MSR_BUF_SIZE 4096
|
#define MSR_BUF_SIZE 4096
|
||||||
|
@ -111,8 +108,8 @@ typedef struct {
|
||||||
} KVMMSRHandlers;
|
} KVMMSRHandlers;
|
||||||
|
|
||||||
static void kvm_init_msrs(X86CPU *cpu);
|
static void kvm_init_msrs(X86CPU *cpu);
|
||||||
static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
|
static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
|
||||||
QEMUWRMSRHandler *wrmsr);
|
QEMUWRMSRHandler *wrmsr);
|
||||||
|
|
||||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||||
KVM_CAP_INFO(SET_TSS_ADDR),
|
KVM_CAP_INFO(SET_TSS_ADDR),
|
||||||
|
@ -564,13 +561,13 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
|
||||||
* be enabled without the in-kernel irqchip
|
* be enabled without the in-kernel irqchip
|
||||||
*/
|
*/
|
||||||
if (!kvm_irqchip_in_kernel()) {
|
if (!kvm_irqchip_in_kernel()) {
|
||||||
ret &= ~(1U << KVM_FEATURE_PV_UNHALT);
|
ret &= ~CPUID_KVM_PV_UNHALT;
|
||||||
}
|
}
|
||||||
if (kvm_irqchip_is_split()) {
|
if (kvm_irqchip_is_split()) {
|
||||||
ret |= 1U << KVM_FEATURE_MSI_EXT_DEST_ID;
|
ret |= CPUID_KVM_MSI_EXT_DEST_ID;
|
||||||
}
|
}
|
||||||
} else if (function == KVM_CPUID_FEATURES && reg == R_EDX) {
|
} else if (function == KVM_CPUID_FEATURES && reg == R_EDX) {
|
||||||
ret |= 1U << KVM_HINTS_REALTIME;
|
ret |= CPUID_KVM_HINTS_REALTIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_machine->cgs) {
|
if (current_machine->cgs) {
|
||||||
|
@ -2617,10 +2614,7 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu,
|
||||||
uint32_t msr,
|
uint32_t msr,
|
||||||
uint64_t *val)
|
uint64_t *val)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
*val = cpu_x86_get_msr_core_thread_count(cpu);
|
||||||
|
|
||||||
*val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */
|
|
||||||
*val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2939,7 +2933,6 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||||
struct KVMMsrEnergy *r = &s->msr_energy;
|
struct KVMMsrEnergy *r = &s->msr_energy;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sanity check
|
* Sanity check
|
||||||
|
@ -2949,13 +2942,11 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
if (!is_host_cpu_intel()) {
|
if (!is_host_cpu_intel()) {
|
||||||
error_report("The RAPL feature can only be enabled on hosts "
|
error_report("The RAPL feature can only be enabled on hosts "
|
||||||
"with Intel CPU models");
|
"with Intel CPU models");
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_rapl_enabled()) {
|
if (!is_rapl_enabled()) {
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the virtual topology */
|
/* Retrieve the virtual topology */
|
||||||
|
@ -2977,16 +2968,14 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
r->host_topo.maxcpus = vmsr_get_maxcpus();
|
r->host_topo.maxcpus = vmsr_get_maxcpus();
|
||||||
if (r->host_topo.maxcpus == 0) {
|
if (r->host_topo.maxcpus == 0) {
|
||||||
error_report("host max cpus = 0");
|
error_report("host max cpus = 0");
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Max number of packages on the host */
|
/* Max number of packages on the host */
|
||||||
r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus);
|
r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus);
|
||||||
if (r->host_topo.maxpkgs == 0) {
|
if (r->host_topo.maxpkgs == 0) {
|
||||||
error_report("host max pkgs = 0");
|
error_report("host max pkgs = 0");
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for each package on the host */
|
/* Allocate memory for each package on the host */
|
||||||
|
@ -2998,8 +2987,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
for (int i = 0; i < r->host_topo.maxpkgs; i++) {
|
for (int i = 0; i < r->host_topo.maxpkgs; i++) {
|
||||||
if (r->host_topo.pkg_cpu_count[i] == 0) {
|
if (r->host_topo.pkg_cpu_count[i] == 0) {
|
||||||
error_report("cpu per packages = 0 on package_%d", i);
|
error_report("cpu per packages = 0 on package_%d", i);
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3016,8 +3004,7 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
|
|
||||||
if (s->msr_energy.sioc == NULL) {
|
if (s->msr_energy.sioc == NULL) {
|
||||||
error_report("vmsr socket opening failed");
|
error_report("vmsr socket opening failed");
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Those MSR values should not change */
|
/* Those MSR values should not change */
|
||||||
|
@ -3029,15 +3016,13 @@ static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms)
|
||||||
s->msr_energy.sioc);
|
s->msr_energy.sioc);
|
||||||
if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) {
|
if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) {
|
||||||
error_report("can't read any virtual msr");
|
error_report("can't read any virtual msr");
|
||||||
ret = 1;
|
return -1;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_thread_create(&r->msr_thr, "kvm-msr",
|
qemu_thread_create(&r->msr_thr, "kvm-msr",
|
||||||
kvm_msr_energy_thread,
|
kvm_msr_energy_thread,
|
||||||
s, QEMU_THREAD_JOINABLE);
|
s, QEMU_THREAD_JOINABLE);
|
||||||
out:
|
return 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_get_default_type(MachineState *ms)
|
int kvm_arch_get_default_type(MachineState *ms)
|
||||||
|
@ -3103,10 +3088,7 @@ static int kvm_vm_set_tss_addr(KVMState *s, uint64_t tss_base)
|
||||||
static int kvm_vm_enable_disable_exits(KVMState *s)
|
static int kvm_vm_enable_disable_exits(KVMState *s)
|
||||||
{
|
{
|
||||||
int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS);
|
int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS);
|
||||||
/* Work around for kernel header with a typo. TODO: fix header and drop. */
|
|
||||||
#if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT)
|
|
||||||
#define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL
|
|
||||||
#endif
|
|
||||||
if (disable_exits) {
|
if (disable_exits) {
|
||||||
disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT |
|
disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT |
|
||||||
KVM_X86_DISABLE_EXITS_HLT |
|
KVM_X86_DISABLE_EXITS_HLT |
|
||||||
|
@ -3156,59 +3138,64 @@ static int kvm_vm_enable_notify_vmexit(KVMState *s)
|
||||||
|
|
||||||
static int kvm_vm_enable_userspace_msr(KVMState *s)
|
static int kvm_vm_enable_userspace_msr(KVMState *s)
|
||||||
{
|
{
|
||||||
int ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
|
int ret;
|
||||||
KVM_MSR_EXIT_REASON_FILTER);
|
|
||||||
|
ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
|
||||||
|
KVM_MSR_EXIT_REASON_FILTER);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("Could not enable user space MSRs: %s",
|
error_report("Could not enable user space MSRs: %s",
|
||||||
strerror(-ret));
|
strerror(-ret));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kvm_filter_msr(s, MSR_CORE_THREAD_COUNT,
|
ret = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT,
|
||||||
kvm_rdmsr_core_thread_count, NULL)) {
|
kvm_rdmsr_core_thread_count, NULL);
|
||||||
error_report("Could not install MSR_CORE_THREAD_COUNT handler!");
|
if (ret < 0) {
|
||||||
|
error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s",
|
||||||
|
strerror(-ret));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_vm_enable_energy_msrs(KVMState *s)
|
static int kvm_vm_enable_energy_msrs(KVMState *s)
|
||||||
{
|
{
|
||||||
bool r;
|
int ret;
|
||||||
|
|
||||||
if (s->msr_energy.enable == true) {
|
if (s->msr_energy.enable == true) {
|
||||||
r = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT,
|
ret = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT,
|
||||||
kvm_rdmsr_rapl_power_unit, NULL);
|
kvm_rdmsr_rapl_power_unit, NULL);
|
||||||
if (!r) {
|
if (ret < 0) {
|
||||||
error_report("Could not install MSR_RAPL_POWER_UNIT \
|
error_report("Could not install MSR_RAPL_POWER_UNIT handler: %s",
|
||||||
handler");
|
strerror(-ret));
|
||||||
exit(1);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT,
|
ret = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT,
|
||||||
kvm_rdmsr_pkg_power_limit, NULL);
|
kvm_rdmsr_pkg_power_limit, NULL);
|
||||||
if (!r) {
|
if (ret < 0) {
|
||||||
error_report("Could not install MSR_PKG_POWER_LIMIT \
|
error_report("Could not install MSR_PKG_POWER_LIMIT handler: %s",
|
||||||
handler");
|
strerror(-ret));
|
||||||
exit(1);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = kvm_filter_msr(s, MSR_PKG_POWER_INFO,
|
ret = kvm_filter_msr(s, MSR_PKG_POWER_INFO,
|
||||||
kvm_rdmsr_pkg_power_info, NULL);
|
kvm_rdmsr_pkg_power_info, NULL);
|
||||||
if (!r) {
|
if (ret < 0) {
|
||||||
error_report("Could not install MSR_PKG_POWER_INFO \
|
error_report("Could not install MSR_PKG_POWER_INFO handler: %s",
|
||||||
handler");
|
strerror(-ret));
|
||||||
exit(1);
|
return ret;
|
||||||
}
|
}
|
||||||
r = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS,
|
ret = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS,
|
||||||
kvm_rdmsr_pkg_energy_status, NULL);
|
kvm_rdmsr_pkg_energy_status, NULL);
|
||||||
if (!r) {
|
if (ret < 0) {
|
||||||
error_report("Could not install MSR_PKG_ENERGY_STATUS \
|
error_report("Could not install MSR_PKG_ENERGY_STATUS handler: %s",
|
||||||
handler");
|
strerror(-ret));
|
||||||
exit(1);
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_arch_init(MachineState *ms, KVMState *s)
|
int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||||
|
@ -3275,7 +3262,10 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_get_supported_feature_msrs(s);
|
ret = kvm_get_supported_feature_msrs(s);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
uname(&utsname);
|
uname(&utsname);
|
||||||
lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
|
lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
|
||||||
|
@ -3311,6 +3301,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("kvm: guest stopping CPU not supported: %s",
|
error_report("kvm: guest stopping CPU not supported: %s",
|
||||||
strerror(-ret));
|
strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3342,10 +3333,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->msr_energy.enable == true) {
|
if (s->msr_energy.enable == true) {
|
||||||
kvm_vm_enable_energy_msrs(s);
|
ret = kvm_vm_enable_energy_msrs(s);
|
||||||
if (kvm_msr_energy_thread_init(s, ms)) {
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = kvm_msr_energy_thread_init(s, ms);
|
||||||
|
if (ret < 0) {
|
||||||
error_report("kvm : error RAPL feature requirement not met");
|
error_report("kvm : error RAPL feature requirement not met");
|
||||||
exit(1);
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3976,22 +3972,24 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
|
||||||
*/
|
*/
|
||||||
if (level >= KVM_PUT_RESET_STATE) {
|
if (level >= KVM_PUT_RESET_STATE) {
|
||||||
kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc);
|
kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc);
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr);
|
if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr);
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) {
|
kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr);
|
||||||
|
}
|
||||||
|
if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, env->async_pf_int_msr);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr);
|
kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, env->poll_control_msr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4454,21 +4452,23 @@ static int kvm_get_msrs(X86CPU *cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0);
|
if (env->features[FEAT_KVM] & (CPUID_KVM_CLOCK | CPUID_KVM_CLOCK2)) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0);
|
kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0);
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF_INT)) {
|
kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0);
|
||||||
|
}
|
||||||
|
if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF_INT) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0);
|
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_INT, 0);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_ASYNCPF) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0);
|
kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_PV_EOI) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0);
|
kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_STEAL_TIME) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0);
|
kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0);
|
||||||
}
|
}
|
||||||
if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_POLL_CONTROL)) {
|
if (env->features[FEAT_KVM] & CPUID_KVM_POLL_CONTROL) {
|
||||||
kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1);
|
kvm_msr_entry_add(cpu, MSR_KVM_POLL_CONTROL, 1);
|
||||||
}
|
}
|
||||||
if (has_architectural_pmu_version > 0) {
|
if (has_architectural_pmu_version > 0) {
|
||||||
|
@ -5843,15 +5843,16 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kvm_install_msr_filters(KVMState *s)
|
static int kvm_install_msr_filters(KVMState *s)
|
||||||
{
|
{
|
||||||
uint64_t zero = 0;
|
uint64_t zero = 0;
|
||||||
struct kvm_msr_filter filter = {
|
struct kvm_msr_filter filter = {
|
||||||
.flags = KVM_MSR_FILTER_DEFAULT_ALLOW,
|
.flags = KVM_MSR_FILTER_DEFAULT_ALLOW,
|
||||||
};
|
};
|
||||||
int r, i, j = 0;
|
int i, j = 0;
|
||||||
|
|
||||||
for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) {
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(msr_handlers) != ARRAY_SIZE(filter.ranges));
|
||||||
|
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
|
||||||
KVMMSRHandlers *handler = &msr_handlers[i];
|
KVMMSRHandlers *handler = &msr_handlers[i];
|
||||||
if (handler->msr) {
|
if (handler->msr) {
|
||||||
struct kvm_msr_filter_range *range = &filter.ranges[j++];
|
struct kvm_msr_filter_range *range = &filter.ranges[j++];
|
||||||
|
@ -5873,18 +5874,13 @@ static bool kvm_install_msr_filters(KVMState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
|
return kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
|
||||||
if (r) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
|
static int kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
|
||||||
QEMUWRMSRHandler *wrmsr)
|
QEMUWRMSRHandler *wrmsr)
|
||||||
{
|
{
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
|
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
|
||||||
if (!msr_handlers[i].msr) {
|
if (!msr_handlers[i].msr) {
|
||||||
|
@ -5894,16 +5890,17 @@ static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
|
||||||
.wrmsr = wrmsr,
|
.wrmsr = wrmsr,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!kvm_install_msr_filters(s)) {
|
ret = kvm_install_msr_filters(s);
|
||||||
|
if (ret) {
|
||||||
msr_handlers[i] = (KVMMSRHandlers) { };
|
msr_handlers[i] = (KVMMSRHandlers) { };
|
||||||
return false;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run)
|
static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run)
|
||||||
|
@ -6195,7 +6192,7 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address)
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
env = &X86_CPU(first_cpu)->env;
|
env = &X86_CPU(first_cpu)->env;
|
||||||
if (!(env->features[FEAT_KVM] & (1 << KVM_FEATURE_MSI_EXT_DEST_ID))) {
|
if (!(env->features[FEAT_KVM] & CPUID_KVM_MSI_EXT_DEST_ID)) {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -286,24 +286,25 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v)
|
||||||
gen_op_ld_v(s, op->ot, v, s->A0);
|
gen_op_ld_v(s, op->ot, v, s->A0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) {
|
|
||||||
if (v == s->T0 && decode->e.special == X86_SPECIAL_SExtT0) {
|
|
||||||
tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8);
|
|
||||||
} else {
|
|
||||||
tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (op->ot < MO_TL && v == s->T0 &&
|
} else if (op->ot < MO_TL && v == s->T0 &&
|
||||||
(decode->e.special == X86_SPECIAL_SExtT0 ||
|
(decode->e.special == X86_SPECIAL_SExtT0 ||
|
||||||
decode->e.special == X86_SPECIAL_ZExtT0)) {
|
decode->e.special == X86_SPECIAL_ZExtT0)) {
|
||||||
if (decode->e.special == X86_SPECIAL_SExtT0) {
|
if (op->ot == MO_8 && byte_reg_is_xH(s, op->n)) {
|
||||||
tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN);
|
if (decode->e.special == X86_SPECIAL_SExtT0) {
|
||||||
|
tcg_gen_sextract_tl(v, cpu_regs[op->n - 4], 8, 8);
|
||||||
|
} else {
|
||||||
|
tcg_gen_extract_tl(v, cpu_regs[op->n - 4], 8, 8);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot);
|
if (decode->e.special == X86_SPECIAL_SExtT0) {
|
||||||
|
tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot | MO_SIGN);
|
||||||
|
} else {
|
||||||
|
tcg_gen_ext_tl(v, cpu_regs[op->n], op->ot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_mov_tl(v, cpu_regs[op->n]);
|
gen_op_mov_v_reg(s, op->ot, v, op->n);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case X86_OP_IMM:
|
case X86_OP_IMM:
|
||||||
|
@ -1443,8 +1444,9 @@ static TCGv gen_bt_mask(DisasContext *s, X86DecodedInsn *decode)
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expects truncated bit index in s->T1, 1 << s->T1 in MASK. */
|
/* Expects truncated bit index in COUNT, 1 << COUNT in MASK. */
|
||||||
static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv mask)
|
static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src,
|
||||||
|
TCGv count, TCGv mask)
|
||||||
{
|
{
|
||||||
TCGv cf;
|
TCGv cf;
|
||||||
|
|
||||||
|
@ -1467,15 +1469,34 @@ static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv
|
||||||
decode->cc_src = tcg_temp_new();
|
decode->cc_src = tcg_temp_new();
|
||||||
decode->cc_dst = cpu_cc_dst;
|
decode->cc_dst = cpu_cc_dst;
|
||||||
decode->cc_op = CC_OP_SARB + cc_op_size(s->cc_op);
|
decode->cc_op = CC_OP_SARB + cc_op_size(s->cc_op);
|
||||||
tcg_gen_shr_tl(decode->cc_src, src, s->T1);
|
tcg_gen_shr_tl(decode->cc_src, src, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_BT(DisasContext *s, X86DecodedInsn *decode)
|
static void gen_BT(DisasContext *s, X86DecodedInsn *decode)
|
||||||
{
|
{
|
||||||
TCGv mask = gen_bt_mask(s, decode);
|
TCGv count = s->T1;
|
||||||
|
TCGv mask;
|
||||||
|
|
||||||
gen_bt_flags(s, decode, s->T0, mask);
|
/*
|
||||||
|
* Try to ensure that the rhs of the TSTNE condition is a constant (and a
|
||||||
|
* power of two), as that is more readily available on most TCG backends.
|
||||||
|
*
|
||||||
|
* For immediate bit number gen_bt_mask()'s output is already a constant;
|
||||||
|
* for register bit number, shift the source right and check bit 0.
|
||||||
|
*/
|
||||||
|
if (decode->e.op2 == X86_TYPE_I) {
|
||||||
|
mask = gen_bt_mask(s, decode);
|
||||||
|
} else {
|
||||||
|
MemOp ot = decode->op[1].ot;
|
||||||
|
|
||||||
|
tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1);
|
||||||
|
tcg_gen_shr_tl(s->T0, s->T0, s->T1);
|
||||||
|
|
||||||
|
count = tcg_constant_tl(0);
|
||||||
|
mask = tcg_constant_tl(1);
|
||||||
|
}
|
||||||
|
gen_bt_flags(s, decode, s->T0, count, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_BTC(DisasContext *s, X86DecodedInsn *decode)
|
static void gen_BTC(DisasContext *s, X86DecodedInsn *decode)
|
||||||
|
@ -1491,7 +1512,7 @@ static void gen_BTC(DisasContext *s, X86DecodedInsn *decode)
|
||||||
tcg_gen_xor_tl(s->T0, s->T0, mask);
|
tcg_gen_xor_tl(s->T0, s->T0, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_bt_flags(s, decode, old, mask);
|
gen_bt_flags(s, decode, old, s->T1, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_BTR(DisasContext *s, X86DecodedInsn *decode)
|
static void gen_BTR(DisasContext *s, X86DecodedInsn *decode)
|
||||||
|
@ -1509,7 +1530,7 @@ static void gen_BTR(DisasContext *s, X86DecodedInsn *decode)
|
||||||
tcg_gen_andc_tl(s->T0, s->T0, mask);
|
tcg_gen_andc_tl(s->T0, s->T0, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_bt_flags(s, decode, old, mask);
|
gen_bt_flags(s, decode, old, s->T1, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_BTS(DisasContext *s, X86DecodedInsn *decode)
|
static void gen_BTS(DisasContext *s, X86DecodedInsn *decode)
|
||||||
|
@ -1525,7 +1546,7 @@ static void gen_BTS(DisasContext *s, X86DecodedInsn *decode)
|
||||||
tcg_gen_or_tl(s->T0, s->T0, mask);
|
tcg_gen_or_tl(s->T0, s->T0, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_bt_flags(s, decode, old, mask);
|
gen_bt_flags(s, decode, old, s->T1, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode)
|
static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode)
|
||||||
|
|
|
@ -468,8 +468,7 @@ void helper_rdmsr(CPUX86State *env)
|
||||||
val = x86_cpu->ucode_rev;
|
val = x86_cpu->ucode_rev;
|
||||||
break;
|
break;
|
||||||
case MSR_CORE_THREAD_COUNT: {
|
case MSR_CORE_THREAD_COUNT: {
|
||||||
CPUState *cs = CPU(x86_cpu);
|
val = cpu_x86_get_msr_core_thread_count(x86_cpu);
|
||||||
val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MSR_APIC_START ... MSR_APIC_END: {
|
case MSR_APIC_START ... MSR_APIC_END: {
|
||||||
|
|
|
@ -486,7 +486,7 @@ static inline
|
||||||
void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg)
|
void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg)
|
||||||
{
|
{
|
||||||
if (ot == MO_8 && byte_reg_is_xH(s, reg)) {
|
if (ot == MO_8 && byte_reg_is_xH(s, reg)) {
|
||||||
tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8);
|
tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8);
|
||||||
} else {
|
} else {
|
||||||
tcg_gen_mov_tl(t0, cpu_regs[reg]);
|
tcg_gen_mov_tl(t0, cpu_regs[reg]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue