mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* x86 TCG fixes for 64-bit call gates (Andrew) * qumu-guest-agent freeze-hook tweak (Christian) * pm_smbus improvements (Corey) * Move validation to pre_plug for pc-dimm (David) * Fix memory leaks (Eduardo, Marc-André) * synchronization profiler (Emilio) * Convert the CPU list to RCU (Emilio) * LSI support for PPR Extended Message (George) * vhost-scsi support for protection information (Greg) * Mark mptsas as a storage device in the help (Guenter) * checkpatch tweak cherry-picked from Linux (me) * Typos, cleanups and dead-code removal (Julia, Marc-André) * qemu-pr-helper support for old libmultipath (Murilo) * Annotate fallthroughs (me) * MemoryRegionOps cleanup (me, Peter) * Make s390 qtests independent from libqos, which doesn't actually support it (me) * Make cpu_get_ticks independent from BQL (me) * Introspection fixes (Thomas) * Support QEMU_MODULE_DIR environment variable (ryang) # gpg: Signature made Thu 23 Aug 2018 17:46:30 BST # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # 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 * remotes/bonzini/tags/for-upstream: (69 commits) KVM: cleanup unnecessary #ifdef KVM_CAP_... target/i386: update MPX flags when CPL changes i2c: pm_smbus: Add the ability to force block transfer enable i2c: pm_smbus: Don't delay host status register busy bit when interrupts are enabled i2c: pm_smbus: Add interrupt handling i2c: pm_smbus: Add block transfer capability i2c: pm_smbus: Make the I2C block read command read-only i2c: pm_smbus: Fix the semantics of block I2C transfers i2c: pm_smbus: Clean up some style issues pc-dimm: assign and verify the "addr" property during pre_plug pc: drop memory region alignment check for 0 util/oslib-win32: indicate alignment for qemu_anon_ram_alloc() pc-dimm: assign and verify the "slot" property during pre_plug ipmi: Use proper struct reference for BT vmstate vhost-scsi: expose 't10_pi' property for VIRTIO_SCSI_F_T10_PI vhost-scsi: unify vhost-scsi get_features implementations vhost-user-scsi: move host_features into VHostSCSICommon cpus: allow cpu_get_ticks out of BQL cpus: protect TimerState writes with a spinlock seqlock: add QemuLockable support ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+10
-1
@@ -1696,7 +1696,6 @@ F: qom/
|
||||
X: qom/cpu.c
|
||||
F: tests/check-qom-interface.c
|
||||
F: tests/check-qom-proplist.c
|
||||
F: tests/qom-test.c
|
||||
|
||||
QMP
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
@@ -1708,6 +1707,16 @@ F: scripts/qmp/
|
||||
F: tests/qmp-test.c
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
qtest
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Thomas Huth <thuth@redhat.com>
|
||||
M: Laurent Vivier <lvivier@redhat.com>
|
||||
S: Maintained
|
||||
F: qtest.c
|
||||
F: tests/libqtest.*
|
||||
F: tests/libqos/
|
||||
F: tests/*-test.c
|
||||
|
||||
Register API
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
S: Maintained
|
||||
|
||||
@@ -1639,10 +1639,8 @@ static int kvm_init(MachineState *ms)
|
||||
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
||||
}
|
||||
|
||||
#ifdef KVM_CAP_READONLY_MEM
|
||||
kvm_readonly_mem_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
||||
#endif
|
||||
|
||||
kvm_eventfds_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
||||
|
||||
@@ -3612,6 +3612,7 @@ fi
|
||||
# libmpathpersist probe
|
||||
|
||||
if test "$mpath" != "no" ; then
|
||||
# probe for the new API
|
||||
cat > $TMPC <<EOF
|
||||
#include <libudev.h>
|
||||
#include <mpath_persist.h>
|
||||
@@ -3633,8 +3634,26 @@ int main(void) {
|
||||
EOF
|
||||
if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then
|
||||
mpathpersist=yes
|
||||
mpathpersist_new_api=yes
|
||||
else
|
||||
mpathpersist=no
|
||||
# probe for the old API
|
||||
cat > $TMPC <<EOF
|
||||
#include <libudev.h>
|
||||
#include <mpath_persist.h>
|
||||
unsigned mpath_mx_alloc_len = 1024;
|
||||
int logsink;
|
||||
int main(void) {
|
||||
struct udev *udev = udev_new();
|
||||
mpath_lib_init(udev);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then
|
||||
mpathpersist=yes
|
||||
mpathpersist_new_api=no
|
||||
else
|
||||
mpathpersist=no
|
||||
fi
|
||||
fi
|
||||
else
|
||||
mpathpersist=no
|
||||
@@ -6409,9 +6428,6 @@ if test "$bluez" = "yes" ; then
|
||||
echo "CONFIG_BLUEZ=y" >> $config_host_mak
|
||||
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
|
||||
fi
|
||||
if test "$glib_subprocess" = "yes" ; then
|
||||
echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$gtk" = "yes" ; then
|
||||
echo "CONFIG_GTK=m" >> $config_host_mak
|
||||
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
||||
@@ -6495,6 +6511,9 @@ if test "$virtfs" = "yes" ; then
|
||||
fi
|
||||
if test "$mpath" = "yes" ; then
|
||||
echo "CONFIG_MPATH=y" >> $config_host_mak
|
||||
if test "$mpathpersist_new_api" = "yes"; then
|
||||
echo "CONFIG_MPATH_NEW_API=y" >> $config_host_mak
|
||||
fi
|
||||
fi
|
||||
if test "$vhost_scsi" = "yes" ; then
|
||||
echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ void cpu_list_add(CPUState *cpu)
|
||||
} else {
|
||||
assert(!cpu_index_auto_assigned);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
||||
QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node);
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
|
||||
finish_safe_work(cpu);
|
||||
@@ -101,7 +101,7 @@ void cpu_list_remove(CPUState *cpu)
|
||||
|
||||
assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ)));
|
||||
|
||||
QTAILQ_REMOVE(&cpus, cpu, node);
|
||||
QTAILQ_REMOVE_RCU(&cpus, cpu, node);
|
||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
@@ -121,8 +121,6 @@ static bool all_cpu_threads_idle(void)
|
||||
/* Protected by TimersState seqlock */
|
||||
|
||||
static bool icount_sleep = true;
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
static int icount_time_shift;
|
||||
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
|
||||
#define MAX_ICOUNT_SHIFT 10
|
||||
|
||||
@@ -131,20 +129,27 @@ typedef struct TimersState {
|
||||
int64_t cpu_ticks_prev;
|
||||
int64_t cpu_ticks_offset;
|
||||
|
||||
/* cpu_clock_offset can be read out of BQL, so protect it with
|
||||
* this lock.
|
||||
/* Protect fields that can be respectively read outside the
|
||||
* BQL, and written from multiple threads.
|
||||
*/
|
||||
QemuSeqLock vm_clock_seqlock;
|
||||
int64_t cpu_clock_offset;
|
||||
int32_t cpu_ticks_enabled;
|
||||
int64_t dummy;
|
||||
QemuSpin vm_clock_lock;
|
||||
|
||||
int16_t cpu_ticks_enabled;
|
||||
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
int16_t icount_time_shift;
|
||||
|
||||
/* Compensate for varying guest execution speed. */
|
||||
int64_t qemu_icount_bias;
|
||||
|
||||
int64_t vm_clock_warp_start;
|
||||
int64_t cpu_clock_offset;
|
||||
|
||||
/* Only written by TCG thread */
|
||||
int64_t qemu_icount;
|
||||
|
||||
/* for adjusting icount */
|
||||
int64_t vm_clock_warp_start;
|
||||
QEMUTimer *icount_rt_timer;
|
||||
QEMUTimer *icount_vm_timer;
|
||||
QEMUTimer *icount_warp_timer;
|
||||
@@ -245,16 +250,19 @@ void cpu_update_icount(CPUState *cpu)
|
||||
int64_t executed = cpu_get_icount_executed(cpu);
|
||||
cpu->icount_budget -= executed;
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
#endif
|
||||
atomic_set__nocheck(&timers_state.qemu_icount,
|
||||
atomic_read__nocheck(&timers_state.qemu_icount) +
|
||||
executed);
|
||||
#else /* FIXME: we need 64bit atomics to do this safely */
|
||||
timers_state.qemu_icount += executed;
|
||||
timers_state.qemu_icount + executed);
|
||||
#ifndef CONFIG_ATOMIC64
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
static int64_t cpu_get_icount_raw_locked(void)
|
||||
{
|
||||
CPUState *cpu = current_cpu;
|
||||
|
||||
@@ -266,20 +274,30 @@ int64_t cpu_get_icount_raw(void)
|
||||
/* Take into account what has run */
|
||||
cpu_update_icount(cpu);
|
||||
}
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
/* The read is protected by the seqlock, so __nocheck is okay. */
|
||||
return atomic_read__nocheck(&timers_state.qemu_icount);
|
||||
#else /* FIXME: we need 64bit atomics to do this safely */
|
||||
return timers_state.qemu_icount;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t cpu_get_icount_locked(void)
|
||||
{
|
||||
int64_t icount = cpu_get_icount_raw_locked();
|
||||
return atomic_read__nocheck(&timers_state.qemu_icount_bias) + cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
{
|
||||
int64_t icount;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
icount = cpu_get_icount_raw_locked();
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return icount;
|
||||
}
|
||||
|
||||
/* Return the virtual CPU time, based on the instruction counter. */
|
||||
static int64_t cpu_get_icount_locked(void)
|
||||
{
|
||||
int64_t icount = cpu_get_icount_raw();
|
||||
return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount(void)
|
||||
{
|
||||
int64_t icount;
|
||||
@@ -295,14 +313,29 @@ int64_t cpu_get_icount(void)
|
||||
|
||||
int64_t cpu_icount_to_ns(int64_t icount)
|
||||
{
|
||||
return icount << icount_time_shift;
|
||||
return icount << atomic_read(&timers_state.icount_time_shift);
|
||||
}
|
||||
|
||||
static int64_t cpu_get_ticks_locked(void)
|
||||
{
|
||||
int64_t ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Non increasing ticks may happen if the host uses software suspend. */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
ticks = timers_state.cpu_ticks_prev;
|
||||
}
|
||||
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
return ticks;
|
||||
}
|
||||
|
||||
/* return the time elapsed in VM between vm_start and vm_stop. Unless
|
||||
* icount is active, cpu_get_ticks() uses units of the host CPU cycle
|
||||
* counter.
|
||||
*
|
||||
* Caller must hold the BQL
|
||||
*/
|
||||
int64_t cpu_get_ticks(void)
|
||||
{
|
||||
@@ -312,19 +345,9 @@ int64_t cpu_get_ticks(void)
|
||||
return cpu_get_icount();
|
||||
}
|
||||
|
||||
ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Note: non increasing ticks may happen if the host uses
|
||||
software suspend */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
ticks = timers_state.cpu_ticks_prev;
|
||||
}
|
||||
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
qemu_spin_lock(&timers_state.vm_clock_lock);
|
||||
ticks = cpu_get_ticks_locked();
|
||||
qemu_spin_unlock(&timers_state.vm_clock_lock);
|
||||
return ticks;
|
||||
}
|
||||
|
||||
@@ -361,14 +384,15 @@ int64_t cpu_get_clock(void)
|
||||
*/
|
||||
void cpu_enable_ticks(void)
|
||||
{
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset -= get_clock();
|
||||
timers_state.cpu_ticks_enabled = 1;
|
||||
}
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
/* disable cpu_get_ticks() : the clock is stopped. You must not call
|
||||
@@ -377,14 +401,15 @@ void cpu_enable_ticks(void)
|
||||
*/
|
||||
void cpu_disable_ticks(void)
|
||||
{
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset += cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset = cpu_get_clock_locked();
|
||||
timers_state.cpu_ticks_enabled = 0;
|
||||
}
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
/* Correlation between real and virtual time is always going to be
|
||||
@@ -407,7 +432,8 @@ static void icount_adjust(void)
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
cur_time = cpu_get_clock_locked();
|
||||
cur_icount = cpu_get_icount_locked();
|
||||
|
||||
@@ -415,20 +441,24 @@ static void icount_adjust(void)
|
||||
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
|
||||
if (delta > 0
|
||||
&& last_delta + ICOUNT_WOBBLE < delta * 2
|
||||
&& icount_time_shift > 0) {
|
||||
&& timers_state.icount_time_shift > 0) {
|
||||
/* The guest is getting too far ahead. Slow time down. */
|
||||
icount_time_shift--;
|
||||
atomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift - 1);
|
||||
}
|
||||
if (delta < 0
|
||||
&& last_delta - ICOUNT_WOBBLE > delta * 2
|
||||
&& icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
&& timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
/* The guest is getting too far behind. Speed time up. */
|
||||
icount_time_shift++;
|
||||
atomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift + 1);
|
||||
}
|
||||
last_delta = delta;
|
||||
timers_state.qemu_icount_bias = cur_icount
|
||||
- (timers_state.qemu_icount << icount_time_shift);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
cur_icount - (timers_state.qemu_icount
|
||||
<< timers_state.icount_time_shift));
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
static void icount_adjust_rt(void *opaque)
|
||||
@@ -448,7 +478,8 @@ static void icount_adjust_vm(void *opaque)
|
||||
|
||||
static int64_t qemu_icount_round(int64_t count)
|
||||
{
|
||||
return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
|
||||
int shift = atomic_read(&timers_state.icount_time_shift);
|
||||
return (count + (1 << shift) - 1) >> shift;
|
||||
}
|
||||
|
||||
static void icount_warp_rt(void)
|
||||
@@ -468,7 +499,8 @@ static void icount_warp_rt(void)
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (runstate_is_running()) {
|
||||
int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT,
|
||||
cpu_get_clock_locked());
|
||||
@@ -484,10 +516,12 @@ static void icount_warp_rt(void)
|
||||
int64_t delta = clock - cur_icount;
|
||||
warp_delta = MIN(warp_delta, delta);
|
||||
}
|
||||
timers_state.qemu_icount_bias += warp_delta;
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp_delta);
|
||||
}
|
||||
timers_state.vm_clock_warp_start = -1;
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
|
||||
if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
@@ -512,9 +546,12 @@ void qtest_clock_warp(int64_t dest)
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
timers_state.qemu_icount_bias += warp;
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
|
||||
@@ -581,9 +618,12 @@ void qemu_start_warp_timer(void)
|
||||
* It is useful when we want a deterministic execution time,
|
||||
* isolated from host latencies.
|
||||
*/
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
timers_state.qemu_icount_bias += deadline;
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set__nocheck(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + deadline);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
} else {
|
||||
/*
|
||||
@@ -594,12 +634,14 @@ void qemu_start_warp_timer(void)
|
||||
* you will not be sending network packets continuously instead of
|
||||
* every 100ms.
|
||||
*/
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
if (timers_state.vm_clock_warp_start == -1
|
||||
|| timers_state.vm_clock_warp_start > clock) {
|
||||
timers_state.vm_clock_warp_start = clock;
|
||||
}
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
timer_mod_anticipate(timers_state.icount_warp_timer,
|
||||
clock + deadline);
|
||||
}
|
||||
@@ -700,7 +742,7 @@ static const VMStateDescription vmstate_timers = {
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(cpu_ticks_offset, TimersState),
|
||||
VMSTATE_INT64(dummy, TimersState),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
@@ -812,7 +854,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
}
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
errno = 0;
|
||||
icount_time_shift = strtol(option, &rem_str, 0);
|
||||
timers_state.icount_time_shift = strtol(option, &rem_str, 0);
|
||||
if (errno != 0 || *rem_str != '\0' || !strlen(option)) {
|
||||
error_setg(errp, "icount: Invalid shift value");
|
||||
}
|
||||
@@ -828,7 +870,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
|
||||
/* 125MIPS seems a reasonable initial guess at the guest speed.
|
||||
It will be corrected fairly quickly anyway. */
|
||||
icount_time_shift = 3;
|
||||
timers_state.icount_time_shift = 3;
|
||||
|
||||
/* Have both realtime and virtual time triggers for speed adjustment.
|
||||
The realtime trigger catches emulated time passing too slowly,
|
||||
@@ -1491,7 +1533,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
atomic_mb_set(&cpu->exit_request, 0);
|
||||
}
|
||||
|
||||
qemu_tcg_rr_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus));
|
||||
qemu_tcg_rr_wait_io_event(cpu ? cpu : first_cpu);
|
||||
deal_with_unplugged_cpus();
|
||||
}
|
||||
|
||||
@@ -1762,10 +1804,16 @@ bool qemu_mutex_iothread_locked(void)
|
||||
return iothread_locked;
|
||||
}
|
||||
|
||||
void qemu_mutex_lock_iothread(void)
|
||||
/*
|
||||
* The BQL is taken from so many places that it is worth profiling the
|
||||
* callers directly, instead of funneling them all through a single function.
|
||||
*/
|
||||
void qemu_mutex_lock_iothread_impl(const char *file, int line)
|
||||
{
|
||||
QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func);
|
||||
|
||||
g_assert(!qemu_mutex_iothread_locked());
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
bql_lock(&qemu_global_mutex, file, line);
|
||||
iothread_locked = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1623,6 +1623,7 @@ print_insn_arg (const char *d,
|
||||
|
||||
case 'X':
|
||||
place = '8';
|
||||
/* fall through */
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
case 'W':
|
||||
|
||||
@@ -1742,7 +1742,7 @@ static void dump_init(DumpState *s, int fd, bool has_format,
|
||||
warn_report("guest note is not present");
|
||||
} else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) {
|
||||
warn_report("guest note size is invalid: %" PRIu32, size);
|
||||
} else if (format != VMCOREINFO_FORMAT_ELF) {
|
||||
} else if (format != FW_CFG_VMCOREINFO_FORMAT_ELF) {
|
||||
warn_report("guest note format is unsupported: %" PRIu16, format);
|
||||
} else {
|
||||
s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */
|
||||
|
||||
@@ -297,6 +297,28 @@ STEXI
|
||||
@item info opcount
|
||||
@findex info opcount
|
||||
Show dynamic compiler opcode counters
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "sync-profile",
|
||||
.args_type = "mean:-m,no_coalesce:-n,max:i?",
|
||||
.params = "[-m] [-n] [max]",
|
||||
.help = "show synchronization profiling info, up to max entries "
|
||||
"(default: 10), sorted by total wait time. (-m: sort by "
|
||||
"mean wait time; -n: do not coalesce objects with the "
|
||||
"same call site)",
|
||||
.cmd = hmp_info_sync_profile,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item info sync-profile [-m|-n] [@var{max}]
|
||||
@findex info sync-profile
|
||||
Show synchronization profiling info, up to @var{max} entries (default: 10),
|
||||
sorted by total wait time.
|
||||
-m: sort by mean wait time
|
||||
-n: do not coalesce objects with the same call site
|
||||
When different objects that share the same call site are coalesced, the "Object"
|
||||
field shows---enclosed in brackets---the number of objects being coalesced.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
||||
@@ -643,6 +643,21 @@ sendkey ctrl-alt-f1
|
||||
|
||||
This command is useful to send keys that your graphical user interface
|
||||
intercepts at low level, such as @code{ctrl-alt-f1} in X Window.
|
||||
ETEXI
|
||||
{
|
||||
.name = "sync-profile",
|
||||
.args_type = "op:s?",
|
||||
.params = "[on|off|reset]",
|
||||
.help = "enable, disable or reset synchronization profiling. "
|
||||
"With no arguments, prints whether profiling is on or off.",
|
||||
.cmd = hmp_sync_profile,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item sync-profile [on|off|reset]
|
||||
@findex sync-profile
|
||||
Enable, disable or reset synchronization profiling. With no arguments, prints
|
||||
whether profiling is on or off.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
||||
@@ -1062,6 +1062,30 @@ void hmp_stop(Monitor *mon, const QDict *qdict)
|
||||
qmp_stop(NULL);
|
||||
}
|
||||
|
||||
void hmp_sync_profile(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *op = qdict_get_try_str(qdict, "op");
|
||||
|
||||
if (op == NULL) {
|
||||
bool on = qsp_is_enabled();
|
||||
|
||||
monitor_printf(mon, "sync-profile is %s\n", on ? "on" : "off");
|
||||
return;
|
||||
}
|
||||
if (!strcmp(op, "on")) {
|
||||
qsp_enable();
|
||||
} else if (!strcmp(op, "off")) {
|
||||
qsp_disable();
|
||||
} else if (!strcmp(op, "reset")) {
|
||||
qsp_reset();
|
||||
} else {
|
||||
Error *err = NULL;
|
||||
|
||||
error_setg(&err, QERR_INVALID_PARAMETER, op);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
}
|
||||
|
||||
void hmp_system_reset(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
qmp_system_reset(NULL);
|
||||
|
||||
@@ -42,6 +42,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict);
|
||||
void hmp_info_iothreads(Monitor *mon, const QDict *qdict);
|
||||
void hmp_quit(Monitor *mon, const QDict *qdict);
|
||||
void hmp_stop(Monitor *mon, const QDict *qdict);
|
||||
void hmp_sync_profile(Monitor *mon, const QDict *qdict);
|
||||
void hmp_system_reset(Monitor *mon, const QDict *qdict);
|
||||
void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
|
||||
void hmp_exit_preconfig(Monitor *mon, const QDict *qdict);
|
||||
|
||||
+1
-1
@@ -512,7 +512,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
|
||||
pci_conf[0x90] = s->smb_io_base | 1;
|
||||
pci_conf[0x91] = s->smb_io_base >> 8;
|
||||
pci_conf[0xd2] = 0x09;
|
||||
pm_smbus_init(DEVICE(dev), &s->smb);
|
||||
pm_smbus_init(DEVICE(dev), &s->smb, true);
|
||||
memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1);
|
||||
memory_region_add_subregion(pci_address_space_io(dev),
|
||||
s->smb_io_base, &s->smb.io);
|
||||
|
||||
+1
-1
@@ -409,7 +409,7 @@ static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr,
|
||||
case MDCNFG ... SA1110:
|
||||
if ((addr & 3) == 0)
|
||||
return s->mm_regs[addr >> 2];
|
||||
|
||||
/* fall through */
|
||||
default:
|
||||
printf("%s: Bad register " REG_FMT "\n", __func__, addr);
|
||||
break;
|
||||
|
||||
@@ -305,6 +305,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
|
||||
|
||||
case 6:
|
||||
as.endianness = 1;
|
||||
/* fall through */
|
||||
case 2:
|
||||
as.fmt = AUD_FMT_S16;
|
||||
s->shift = as.nchannels;
|
||||
|
||||
+25
-210
@@ -474,82 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
uint32_t shift, mask;
|
||||
|
||||
addr = es1370_fixup (s, addr);
|
||||
|
||||
switch (addr) {
|
||||
case ES1370_REG_CONTROL:
|
||||
case ES1370_REG_CONTROL + 1:
|
||||
case ES1370_REG_CONTROL + 2:
|
||||
case ES1370_REG_CONTROL + 3:
|
||||
shift = (addr - ES1370_REG_CONTROL) << 3;
|
||||
mask = 0xff << shift;
|
||||
val = (s->ctl & ~mask) | ((val & 0xff) << shift);
|
||||
es1370_update_voices (s, val, s->sctl);
|
||||
print_ctl (val);
|
||||
break;
|
||||
case ES1370_REG_MEMPAGE:
|
||||
s->mempage = val;
|
||||
break;
|
||||
case ES1370_REG_SERIAL_CONTROL:
|
||||
case ES1370_REG_SERIAL_CONTROL + 1:
|
||||
case ES1370_REG_SERIAL_CONTROL + 2:
|
||||
case ES1370_REG_SERIAL_CONTROL + 3:
|
||||
shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
|
||||
mask = 0xff << shift;
|
||||
val = (s->sctl & ~mask) | ((val & 0xff) << shift);
|
||||
es1370_maybe_lower_irq (s, val);
|
||||
es1370_update_voices (s, s->ctl, val);
|
||||
print_sctl (val);
|
||||
break;
|
||||
default:
|
||||
lwarn ("writeb %#x <- %#x\n", addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void es1370_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
addr = es1370_fixup (s, addr);
|
||||
uint32_t shift, mask;
|
||||
struct chan *d = &s->chan[0];
|
||||
|
||||
switch (addr) {
|
||||
case ES1370_REG_CODEC:
|
||||
dolog ("ignored codec write address %#x, data %#x\n",
|
||||
(val >> 8) & 0xff, val & 0xff);
|
||||
s->codec = val;
|
||||
break;
|
||||
|
||||
case ES1370_REG_CONTROL:
|
||||
case ES1370_REG_CONTROL + 2:
|
||||
shift = (addr != ES1370_REG_CONTROL) << 4;
|
||||
mask = 0xffff << shift;
|
||||
val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
|
||||
es1370_update_voices (s, val, s->sctl);
|
||||
print_ctl (val);
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_SCOUNT:
|
||||
d->scount = (d->scount & ~0xffff) | (val & 0xffff);
|
||||
break;
|
||||
|
||||
default:
|
||||
lwarn ("writew %#x <- %#x\n", addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
struct chan *d = &s->chan[0];
|
||||
@@ -572,21 +497,19 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
print_sctl (val);
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_SCOUNT:
|
||||
case ES1370_REG_DAC2_SCOUNT:
|
||||
case ES1370_REG_ADC_SCOUNT:
|
||||
d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2;
|
||||
d->scount = (val & 0xffff) | (d->scount & ~0xffff);
|
||||
ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n",
|
||||
d - &s->chan[0], val >> 16, (val & 0xffff));
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMEADR:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMEADR:
|
||||
case ES1370_REG_DAC2_FRAMEADR:
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
|
||||
d->frame_addr = val;
|
||||
ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val);
|
||||
break;
|
||||
@@ -598,11 +521,10 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
lwarn ("writing to phantom frame address %#x\n", val);
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMECNT:
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
|
||||
d->frame_cnt = val;
|
||||
d->leftover = 0;
|
||||
ldebug ("chan %td frame count %d, buffer size %d\n",
|
||||
@@ -615,84 +537,7 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t es1370_readb(void *opaque, uint32_t addr)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr = es1370_fixup (s, addr);
|
||||
|
||||
switch (addr) {
|
||||
case 0x1b: /* Legacy */
|
||||
lwarn ("Attempt to read from legacy register\n");
|
||||
val = 5;
|
||||
break;
|
||||
case ES1370_REG_MEMPAGE:
|
||||
val = s->mempage;
|
||||
break;
|
||||
case ES1370_REG_CONTROL + 0:
|
||||
case ES1370_REG_CONTROL + 1:
|
||||
case ES1370_REG_CONTROL + 2:
|
||||
case ES1370_REG_CONTROL + 3:
|
||||
val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
|
||||
break;
|
||||
case ES1370_REG_STATUS + 0:
|
||||
case ES1370_REG_STATUS + 1:
|
||||
case ES1370_REG_STATUS + 2:
|
||||
case ES1370_REG_STATUS + 3:
|
||||
val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
|
||||
break;
|
||||
default:
|
||||
val = ~0;
|
||||
lwarn ("readb %#x -> %#x\n", addr, val);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t es1370_readw(void *opaque, uint32_t addr)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
struct chan *d = &s->chan[0];
|
||||
uint32_t val;
|
||||
|
||||
addr = es1370_fixup (s, addr);
|
||||
|
||||
switch (addr) {
|
||||
case ES1370_REG_ADC_SCOUNT + 2:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_SCOUNT + 2:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_SCOUNT + 2:
|
||||
val = d->scount >> 16;
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMECNT:
|
||||
val = d->frame_cnt & 0xffff;
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMECNT + 2:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMECNT + 2:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMECNT + 2:
|
||||
val = d->frame_cnt >> 16;
|
||||
break;
|
||||
|
||||
default:
|
||||
val = ~0;
|
||||
lwarn ("readw %#x -> %#x\n", addr, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t es1370_readl(void *opaque, uint32_t addr)
|
||||
static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ES1370State *s = opaque;
|
||||
uint32_t val;
|
||||
@@ -717,11 +562,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
|
||||
val = s->sctl;
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_SCOUNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_SCOUNT:
|
||||
case ES1370_REG_DAC2_SCOUNT:
|
||||
case ES1370_REG_ADC_SCOUNT:
|
||||
d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2;
|
||||
val = d->scount;
|
||||
#ifdef DEBUG_ES1370
|
||||
{
|
||||
@@ -735,11 +579,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMECNT:
|
||||
case ES1370_REG_DAC2_FRAMECNT:
|
||||
case ES1370_REG_ADC_FRAMECNT:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
|
||||
val = d->frame_cnt;
|
||||
#ifdef DEBUG_ES1370
|
||||
{
|
||||
@@ -753,11 +596,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr)
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d++;
|
||||
case ES1370_REG_DAC2_FRAMEADR:
|
||||
d++;
|
||||
case ES1370_REG_DAC1_FRAMEADR:
|
||||
case ES1370_REG_DAC2_FRAMEADR:
|
||||
case ES1370_REG_ADC_FRAMEADR:
|
||||
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
|
||||
val = d->frame_addr;
|
||||
break;
|
||||
|
||||
@@ -908,44 +750,17 @@ static void es1370_adc_callback (void *opaque, int avail)
|
||||
es1370_run_channel (s, ADC_CHANNEL, avail);
|
||||
}
|
||||
|
||||
static uint64_t es1370_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return es1370_readb(opaque, addr);
|
||||
case 2:
|
||||
return es1370_readw(opaque, addr);
|
||||
case 4:
|
||||
return es1370_readl(opaque, addr);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void es1370_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
es1370_writeb(opaque, addr, val);
|
||||
break;
|
||||
case 2:
|
||||
es1370_writew(opaque, addr, val);
|
||||
break;
|
||||
case 4:
|
||||
es1370_writel(opaque, addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps es1370_io_ops = {
|
||||
.read = es1370_read,
|
||||
.write = es1370_write,
|
||||
.impl = {
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
|
||||
GUSregb(IRQStatReg2x6) = 0x10;
|
||||
GUS_irqrequest(state, state->gusirq, 1);
|
||||
}
|
||||
/* fall through */
|
||||
case 0x20D: /* SB2xCd no IRQ */
|
||||
GUSregb(SB2xCd) = (uint8_t) data;
|
||||
break;
|
||||
|
||||
+8
-3
@@ -741,10 +741,15 @@ static void complete (SB16State *s)
|
||||
ldebug ("set time const %d\n", s->time_const);
|
||||
break;
|
||||
|
||||
case 0x42: /* FT2 sets output freq with this, go figure */
|
||||
qemu_log_mask(LOG_UNIMP, "cmd 0x42 might not do what it think it"
|
||||
" should\n");
|
||||
case 0x41:
|
||||
case 0x42:
|
||||
/*
|
||||
* 0x41 is documented as setting the output sample rate,
|
||||
* and 0x42 the input sample rate, but in fact SB16 hardware
|
||||
* seems to have only a single sample rate under the hood,
|
||||
* and FT2 sets output freq with this (go figure). Compare:
|
||||
* http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate
|
||||
*/
|
||||
s->freq = dsp_get_hilo (s);
|
||||
ldebug ("set freq %d\n", s->freq);
|
||||
break;
|
||||
|
||||
@@ -232,6 +232,7 @@ static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
s->b[s->dac_index] = regval;
|
||||
/* Index autoincrement */
|
||||
s->dac_index = (s->dac_index + 1) & 0xff;
|
||||
/* fall through */
|
||||
default:
|
||||
s->dac_state = 0;
|
||||
break;
|
||||
|
||||
@@ -1426,7 +1426,8 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
|
||||
s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
|
||||
break;
|
||||
case 0x07: // Extended Sequencer Mode
|
||||
cirrus_update_memory_access(s);
|
||||
cirrus_update_memory_access(s);
|
||||
/* fall through */
|
||||
case 0x08: // EEPROM Control
|
||||
case 0x09: // Scratch Register 0
|
||||
case 0x0a: // Scratch Register 1
|
||||
|
||||
+214
-40
@@ -22,8 +22,6 @@
|
||||
#include "hw/i2c/pm_smbus.h"
|
||||
#include "hw/i2c/smbus.h"
|
||||
|
||||
/* no save/load? */
|
||||
|
||||
#define SMBHSTSTS 0x00
|
||||
#define SMBHSTCNT 0x02
|
||||
#define SMBHSTCMD 0x03
|
||||
@@ -31,20 +29,40 @@
|
||||
#define SMBHSTDAT0 0x05
|
||||
#define SMBHSTDAT1 0x06
|
||||
#define SMBBLKDAT 0x07
|
||||
#define SMBAUXCTL 0x0d
|
||||
|
||||
#define STS_HOST_BUSY (1)
|
||||
#define STS_INTR (1<<1)
|
||||
#define STS_DEV_ERR (1<<2)
|
||||
#define STS_BUS_ERR (1<<3)
|
||||
#define STS_FAILED (1<<4)
|
||||
#define STS_SMBALERT (1<<5)
|
||||
#define STS_INUSE_STS (1<<6)
|
||||
#define STS_BYTE_DONE (1<<7)
|
||||
#define STS_HOST_BUSY (1 << 0)
|
||||
#define STS_INTR (1 << 1)
|
||||
#define STS_DEV_ERR (1 << 2)
|
||||
#define STS_BUS_ERR (1 << 3)
|
||||
#define STS_FAILED (1 << 4)
|
||||
#define STS_SMBALERT (1 << 5)
|
||||
#define STS_INUSE_STS (1 << 6)
|
||||
#define STS_BYTE_DONE (1 << 7)
|
||||
/* Signs of successfully transaction end :
|
||||
* ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR )
|
||||
*/
|
||||
|
||||
//#define DEBUG
|
||||
#define CTL_INTREN (1 << 0)
|
||||
#define CTL_KILL (1 << 1)
|
||||
#define CTL_LAST_BYTE (1 << 5)
|
||||
#define CTL_START (1 << 6)
|
||||
#define CTL_PEC_EN (1 << 7)
|
||||
#define CTL_RETURN_MASK 0x1f
|
||||
|
||||
#define PROT_QUICK 0
|
||||
#define PROT_BYTE 1
|
||||
#define PROT_BYTE_DATA 2
|
||||
#define PROT_WORD_DATA 3
|
||||
#define PROT_PROC_CALL 4
|
||||
#define PROT_BLOCK_DATA 5
|
||||
#define PROT_I2C_BLOCK_READ 6
|
||||
|
||||
#define AUX_PEC (1 << 0)
|
||||
#define AUX_BLK (1 << 1)
|
||||
#define AUX_MASK 0x3
|
||||
|
||||
/*#define DEBUG*/
|
||||
|
||||
#ifdef DEBUG
|
||||
# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
|
||||
@@ -62,19 +80,17 @@ static void smb_transaction(PMSMBus *s)
|
||||
I2CBus *bus = s->smbus;
|
||||
int ret;
|
||||
|
||||
assert(s->smb_stat & STS_HOST_BUSY);
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
|
||||
SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
|
||||
/* Transaction isn't exec if STS_DEV_ERR bit set */
|
||||
if ((s->smb_stat & STS_DEV_ERR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch(prot) {
|
||||
case 0x0:
|
||||
case PROT_QUICK:
|
||||
ret = smbus_quick_command(bus, addr, read);
|
||||
goto done;
|
||||
case 0x1:
|
||||
case PROT_BYTE:
|
||||
if (read) {
|
||||
ret = smbus_receive_byte(bus, addr);
|
||||
goto data8;
|
||||
@@ -82,7 +98,7 @@ static void smb_transaction(PMSMBus *s)
|
||||
ret = smbus_send_byte(bus, addr, cmd);
|
||||
goto done;
|
||||
}
|
||||
case 0x2:
|
||||
case PROT_BYTE_DATA:
|
||||
if (read) {
|
||||
ret = smbus_read_byte(bus, addr, cmd);
|
||||
goto data8;
|
||||
@@ -91,22 +107,73 @@ static void smb_transaction(PMSMBus *s)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case 0x3:
|
||||
case PROT_WORD_DATA:
|
||||
if (read) {
|
||||
ret = smbus_read_word(bus, addr, cmd);
|
||||
goto data16;
|
||||
} else {
|
||||
ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
|
||||
ret = smbus_write_word(bus, addr, cmd,
|
||||
(s->smb_data1 << 8) | s->smb_data0);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
case PROT_I2C_BLOCK_READ:
|
||||
if (read) {
|
||||
ret = smbus_read_block(bus, addr, cmd, s->smb_data);
|
||||
int xfersize = s->smb_data0;
|
||||
if (xfersize > sizeof(s->smb_data)) {
|
||||
xfersize = sizeof(s->smb_data);
|
||||
}
|
||||
ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data,
|
||||
xfersize, false, true);
|
||||
goto data8;
|
||||
} else {
|
||||
ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
|
||||
goto done;
|
||||
/* The manual says the behavior is undefined, just set DEV_ERR. */
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case PROT_BLOCK_DATA:
|
||||
if (read) {
|
||||
ret = smbus_read_block(bus, addr, cmd, s->smb_data,
|
||||
sizeof(s->smb_data), !s->i2c_enable,
|
||||
!s->i2c_enable);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
s->smb_index = 0;
|
||||
s->op_done = false;
|
||||
if (s->smb_auxctl & AUX_BLK) {
|
||||
s->smb_stat |= STS_INTR;
|
||||
} else {
|
||||
s->smb_blkdata = s->smb_data[0];
|
||||
s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
|
||||
}
|
||||
s->smb_data0 = ret;
|
||||
goto out;
|
||||
} else {
|
||||
if (s->smb_auxctl & AUX_BLK) {
|
||||
if (s->smb_index != s->smb_data0) {
|
||||
s->smb_index = 0;
|
||||
goto error;
|
||||
}
|
||||
/* Data is already all written to the queue, just do
|
||||
the operation. */
|
||||
s->smb_index = 0;
|
||||
ret = smbus_write_block(bus, addr, cmd, s->smb_data,
|
||||
s->smb_data0, !s->i2c_enable);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
s->op_done = true;
|
||||
s->smb_stat |= STS_INTR;
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
} else {
|
||||
s->op_done = false;
|
||||
s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
|
||||
s->smb_data[0] = s->smb_blkdata;
|
||||
s->smb_index = 0;
|
||||
ret = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -128,20 +195,35 @@ done:
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
s->smb_stat |= STS_BYTE_DONE | STS_INTR;
|
||||
s->smb_stat |= STS_INTR;
|
||||
out:
|
||||
return;
|
||||
|
||||
error:
|
||||
s->smb_stat |= STS_DEV_ERR;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static void smb_transaction_start(PMSMBus *s)
|
||||
{
|
||||
/* Do not execute immediately the command ; it will be
|
||||
* executed when guest will read SMB_STAT register */
|
||||
s->smb_stat |= STS_HOST_BUSY;
|
||||
if (s->smb_ctl & CTL_INTREN) {
|
||||
smb_transaction(s);
|
||||
} else {
|
||||
/* Do not execute immediately the command; it will be
|
||||
* executed when guest will read SMB_STAT register. This
|
||||
* is to work around a bug in AMIBIOS (that is working
|
||||
* around another bug in some specific hardware) where
|
||||
* it waits for STS_HOST_BUSY to be set before waiting
|
||||
* checking for status. If STS_HOST_BUSY doesn't get
|
||||
* set, it gets stuck. */
|
||||
s->smb_stat |= STS_HOST_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
smb_irq_value(PMSMBus *s)
|
||||
{
|
||||
return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
|
||||
}
|
||||
|
||||
static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
|
||||
@@ -153,13 +235,61 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
|
||||
" val=0x%02" PRIx64 "\n", addr, val);
|
||||
switch(addr) {
|
||||
case SMBHSTSTS:
|
||||
s->smb_stat = (~(val & 0xff)) & s->smb_stat;
|
||||
s->smb_index = 0;
|
||||
s->smb_stat &= ~(val & ~STS_HOST_BUSY);
|
||||
if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
|
||||
uint8_t read = s->smb_addr & 0x01;
|
||||
|
||||
s->smb_index++;
|
||||
if (!read && s->smb_index == s->smb_data0) {
|
||||
uint8_t prot = (s->smb_ctl >> 2) & 0x07;
|
||||
uint8_t cmd = s->smb_cmd;
|
||||
uint8_t addr = s->smb_addr >> 1;
|
||||
int ret;
|
||||
|
||||
if (prot == PROT_I2C_BLOCK_READ) {
|
||||
s->smb_stat |= STS_DEV_ERR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
|
||||
s->smb_data0, !s->i2c_enable);
|
||||
if (ret < 0) {
|
||||
s->smb_stat |= STS_DEV_ERR;
|
||||
goto out;
|
||||
}
|
||||
s->op_done = true;
|
||||
s->smb_stat |= STS_INTR;
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
} else if (!read) {
|
||||
s->smb_data[s->smb_index] = s->smb_blkdata;
|
||||
s->smb_stat |= STS_BYTE_DONE;
|
||||
} else if (s->smb_ctl & CTL_LAST_BYTE) {
|
||||
s->op_done = true;
|
||||
s->smb_blkdata = s->smb_data[s->smb_index];
|
||||
s->smb_index = 0;
|
||||
s->smb_stat |= STS_INTR;
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
} else {
|
||||
s->smb_blkdata = s->smb_data[s->smb_index];
|
||||
s->smb_stat |= STS_BYTE_DONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SMBHSTCNT:
|
||||
s->smb_ctl = val;
|
||||
if (val & 0x40)
|
||||
s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */
|
||||
if (val & CTL_START) {
|
||||
if (!s->op_done) {
|
||||
s->smb_index = 0;
|
||||
s->op_done = true;
|
||||
}
|
||||
smb_transaction_start(s);
|
||||
}
|
||||
if (s->smb_ctl & CTL_KILL) {
|
||||
s->op_done = true;
|
||||
s->smb_index = 0;
|
||||
s->smb_stat |= STS_FAILED;
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
}
|
||||
break;
|
||||
case SMBHSTCMD:
|
||||
s->smb_cmd = val;
|
||||
@@ -174,13 +304,26 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
|
||||
s->smb_data1 = val;
|
||||
break;
|
||||
case SMBBLKDAT:
|
||||
s->smb_data[s->smb_index++] = val;
|
||||
if (s->smb_index > 31)
|
||||
if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
|
||||
s->smb_index = 0;
|
||||
}
|
||||
if (s->smb_auxctl & AUX_BLK) {
|
||||
s->smb_data[s->smb_index++] = val;
|
||||
} else {
|
||||
s->smb_blkdata = val;
|
||||
}
|
||||
break;
|
||||
case SMBAUXCTL:
|
||||
s->smb_auxctl = val & AUX_MASK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (s->set_irq) {
|
||||
s->set_irq(s, smb_irq_value(s));
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
|
||||
@@ -193,12 +336,12 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
|
||||
val = s->smb_stat;
|
||||
if (s->smb_stat & STS_HOST_BUSY) {
|
||||
/* execute command now */
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
smb_transaction(s);
|
||||
}
|
||||
break;
|
||||
case SMBHSTCNT:
|
||||
s->smb_index = 0;
|
||||
val = s->smb_ctl & 0x1f;
|
||||
val = s->smb_ctl & CTL_RETURN_MASK;
|
||||
break;
|
||||
case SMBHSTCMD:
|
||||
val = s->smb_cmd;
|
||||
@@ -213,18 +356,44 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
|
||||
val = s->smb_data1;
|
||||
break;
|
||||
case SMBBLKDAT:
|
||||
val = s->smb_data[s->smb_index++];
|
||||
if (s->smb_index > 31)
|
||||
if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
|
||||
s->smb_index = 0;
|
||||
}
|
||||
if (s->smb_auxctl & AUX_BLK) {
|
||||
val = s->smb_data[s->smb_index++];
|
||||
if (!s->op_done && s->smb_index == s->smb_data0) {
|
||||
s->op_done = true;
|
||||
s->smb_index = 0;
|
||||
s->smb_stat &= ~STS_HOST_BUSY;
|
||||
}
|
||||
} else {
|
||||
val = s->smb_blkdata;
|
||||
}
|
||||
break;
|
||||
case SMBAUXCTL:
|
||||
val = s->smb_auxctl;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val);
|
||||
SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n",
|
||||
addr, val);
|
||||
|
||||
if (s->set_irq) {
|
||||
s->set_irq(s, smb_irq_value(s));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pm_smbus_reset(PMSMBus *s)
|
||||
{
|
||||
s->op_done = true;
|
||||
s->smb_index = 0;
|
||||
s->smb_stat = 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pm_smbus_ops = {
|
||||
.read = smb_ioport_readb,
|
||||
.write = smb_ioport_writeb,
|
||||
@@ -233,9 +402,14 @@ static const MemoryRegionOps pm_smbus_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
|
||||
void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
|
||||
{
|
||||
smb->op_done = true;
|
||||
smb->reset = pm_smbus_reset;
|
||||
smb->smbus = i2c_init_bus(parent, "i2c");
|
||||
if (force_aux_blk) {
|
||||
smb->smb_auxctl |= AUX_BLK;
|
||||
}
|
||||
memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
|
||||
"pm-smbus", 64);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user