aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/acpi/core.c7
-rw-r--r--hw/i386/kvm/apic.c3
-rw-r--r--hw/i386/kvm/clock.c14
-rw-r--r--hw/i386/smbios.c10
-rw-r--r--hw/intc/apic_common.c38
-rw-r--r--hw/scsi/esp-pci.c3
-rw-r--r--hw/scsi/esp.c11
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c9
-rw-r--r--hw/scsi/virtio-scsi.c17
-rw-r--r--include/hw/scsi/esp.h1
-rw-r--r--include/hw/virtio/virtio-scsi.h3
-rw-r--r--target-i386/cpu.h2
-rw-r--r--target-i386/seg_helper.c17
13 files changed, 109 insertions, 26 deletions
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index a7368fb242..51913d6932 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -376,8 +376,11 @@ static void acpi_notify_wakeup(Notifier *notifier, void *data)
/* ACPI PM1a EVT */
uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
{
- int64_t d = acpi_pm_tmr_get_clock();
- if (d >= ar->tmr.overflow_time) {
+ /* Compare ns-clock, not PM timer ticks, because
+ acpi_pm_tmr_update function uses ns for setting the timer. */
+ int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (d >= muldiv64(ar->tmr.overflow_time,
+ get_ticks_per_sec(), PM_TIMER_FREQUENCY)) {
ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
}
return ar->pm1.evt.sts;
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index e873b509a5..271e97f86f 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -175,6 +175,9 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
{
APICCommonState *s = APIC_COMMON(dev);
+ /* Not used by KVM, which uses the CPU mp_state instead. */
+ s->wait_for_sipi = 0;
+
memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi",
APIC_SPACE_SIZE);
diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
index 1ac60d6cdd..58be2bda27 100644
--- a/hw/i386/kvm/clock.c
+++ b/hw/i386/kvm/clock.c
@@ -127,7 +127,21 @@ static void kvmclock_vm_state_change(void *opaque, int running,
}
cpu_synchronize_all_states();
+ /* In theory, the cpu_synchronize_all_states() call above wouldn't
+ * affect the rest of the code, as the VCPU state inside CPUState
+ * is supposed to always match the VCPU state on the kernel side.
+ *
+ * In practice, calling cpu_synchronize_state() too soon will load the
+ * kernel-side APIC state into X86CPU.apic_state too early, APIC state
+ * won't be reloaded later because CPUState.vcpu_dirty==true, and
+ * outdated APIC state may be migrated to another host.
+ *
+ * The real fix would be to make sure outdated APIC state is read
+ * from the kernel again when necessary. While this is not fixed, we
+ * need the cpu_clean_all_dirty() call below.
+ */
cpu_clean_all_dirty();
+
ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
if (ret < 0) {
fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c
index 8a7ad48921..024e59445b 100644
--- a/hw/i386/smbios.c
+++ b/hw/i386/smbios.c
@@ -645,7 +645,7 @@ static void smbios_build_type_4_table(unsigned instance)
static void smbios_build_type_16_table(unsigned dimm_cnt)
{
- ram_addr_t size_kb;
+ uint64_t size_kb;
SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */
@@ -669,10 +669,10 @@ static void smbios_build_type_16_table(unsigned dimm_cnt)
#define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */
#define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */
-static void smbios_build_type_17_table(unsigned instance, ram_addr_t size)
+static void smbios_build_type_17_table(unsigned instance, uint64_t size)
{
char loc_str[128];
- ram_addr_t size_mb;
+ uint64_t size_mb;
SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */
@@ -711,9 +711,9 @@ static void smbios_build_type_17_table(unsigned instance, ram_addr_t size)
}
static void smbios_build_type_19_table(unsigned instance,
- ram_addr_t start, ram_addr_t size)
+ uint64_t start, uint64_t size)
{
- ram_addr_t end, start_kb, end_kb;
+ uint64_t end, start_kb, end_kb;
SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index ce3d903b13..4e62f25edb 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -324,6 +324,19 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
}
+static int apic_pre_load(void *opaque)
+{
+ APICCommonState *s = APIC_COMMON(opaque);
+
+ /* The default is !cpu_is_bsp(s->cpu), but the common value is 0
+ * so that's what apic_common_sipi_needed checks for. Reset to
+ * the value that is assumed when the apic_sipi subsection is
+ * absent.
+ */
+ s->wait_for_sipi = 0;
+ return 0;
+}
+
static void apic_dispatch_pre_save(void *opaque)
{
APICCommonState *s = APIC_COMMON(opaque);
@@ -345,12 +358,30 @@ static int apic_dispatch_post_load(void *opaque, int version_id)
return 0;
}
+static bool apic_common_sipi_needed(void *opaque)
+{
+ APICCommonState *s = APIC_COMMON(opaque);
+ return s->wait_for_sipi != 0;
+}
+
+static const VMStateDescription vmstate_apic_common_sipi = {
+ .name = "apic_sipi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(sipi_vector, APICCommonState),
+ VMSTATE_INT32(wait_for_sipi, APICCommonState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_apic_common = {
.name = "apic",
.version_id = 3,
.minimum_version_id = 3,
.minimum_version_id_old = 1,
.load_state_old = apic_load_old,
+ .pre_load = apic_pre_load,
.pre_save = apic_dispatch_pre_save,
.post_load = apic_dispatch_post_load,
.fields = (VMStateField[]) {
@@ -375,6 +406,13 @@ static const VMStateDescription vmstate_apic_common = {
VMSTATE_INT64(timer_expiry,
APICCommonState), /* open-coded timer state */
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &vmstate_apic_common_sipi,
+ .needed = apic_common_sipi_needed,
+ },
+ VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c
index 82795e68b8..00b7297354 100644
--- a/hw/scsi/esp-pci.c
+++ b/hw/scsi/esp-pci.c
@@ -268,6 +268,9 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
/* update status registers */
pci->dma_regs[DMA_WBC] -= len;
pci->dma_regs[DMA_WAC] += len;
+ if (pci->dma_regs[DMA_WBC] == 0) {
+ pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+ }
}
static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 5ab44d860b..272d13d633 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -364,7 +364,7 @@ void esp_hard_reset(ESPState *s)
{
memset(s->rregs, 0, ESP_REGS);
memset(s->wregs, 0, ESP_REGS);
- s->rregs[ESP_TCHI] = s->chip_id;
+ s->tchi_written = 0;
s->ti_size = 0;
s->ti_rptr = 0;
s->ti_wptr = 0;
@@ -422,6 +422,11 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
esp_lower_irq(s);
return old_val;
+ case ESP_TCHI:
+ /* Return the unique id if the value has never been written */
+ if (!s->tchi_written) {
+ return s->chip_id;
+ }
default:
break;
}
@@ -432,9 +437,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
{
trace_esp_mem_writeb(saddr, s->wregs[saddr], val);
switch (saddr) {
+ case ESP_TCHI:
+ s->tchi_written = true;
+ /* fall through */
case ESP_TCLO:
case ESP_TCMID:
- case ESP_TCHI:
s->rregs[ESP_RSTAT] &= ~STAT_TC;
break;
case ESP_FIFO:
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 9651e6f274..03a1e8cfcf 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -92,9 +92,14 @@ VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s,
void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
{
+ VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent);
+
vring_push(&req->vring->vring, &req->elem,
req->qsgl.size + req->resp_iov.size);
- event_notifier_set(&req->vring->guest_notifier);
+
+ if (vring_should_notify(vdev, &req->vring->vring)) {
+ event_notifier_set(&req->vring->guest_notifier);
+ }
}
static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier)
@@ -230,7 +235,7 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
if (!s->event_vring) {
goto fail_vrings;
}
- s->cmd_vrings = g_malloc0(sizeof(VirtIOSCSIVring) * vs->conf.num_queues);
+ s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues);
for (i = 0; i < vs->conf.num_queues; i++) {
s->cmd_vrings[i] =
virtio_scsi_vring_init(s, vs->cmd_vqs[i],
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index fdcacfd79a..ef485508b1 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -118,6 +118,7 @@ static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov,
static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
unsigned req_size, unsigned resp_size)
{
+ VirtIODevice *vdev = (VirtIODevice *) req->dev;
size_t in_size, out_size;
if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
@@ -130,8 +131,24 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
resp_size) < resp_size) {
return -EINVAL;
}
+
req->resp_size = resp_size;
+ /* Old BIOSes left some padding by mistake after the req_size/resp_size.
+ * As a workaround, always consider the first buffer as the virtio-scsi
+ * request/response, making the payload start at the second element
+ * of the iovec.
+ *
+ * The actual length of the response header, stored in req->resp_size,
+ * does not change.
+ *
+ * TODO: always disable this workaround for virtio 1.0 devices.
+ */
+ if ((vdev->guest_features & VIRTIO_F_ANY_LAYOUT) == 0) {
+ req_size = req->elem.out_sg[0].iov_len;
+ resp_size = req->elem.in_sg[0].iov_len;
+ }
+
out_size = qemu_sgl_concat(req, req->elem.out_sg,
&req->elem.out_addr[0], req->elem.out_num,
req_size);
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index e079fb8d16..6c795276c9 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -22,6 +22,7 @@ struct ESPState {
uint8_t wregs[ESP_REGS];
qemu_irq irq;
uint8_t chip_id;
+ bool tchi_written;
int32_t ti_size;
uint32_t ti_rptr, ti_wptr;
uint32_t status;
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
index 9e1a49c2c1..bf17cc9ea5 100644
--- a/include/hw/virtio/virtio-scsi.h
+++ b/include/hw/virtio/virtio-scsi.h
@@ -209,7 +209,8 @@ typedef struct VirtIOSCSIReq {
/* Note:
* - fields before elem are initialized by virtio_scsi_init_req;
* - elem is uninitialized at the time of allocation.
- * - fields after elem are zeroed by virtio_scsi_init_req.
+ * - fields after elem (except the ending cdb[]) are zeroed by
+ * virtio_scsi_init_req.
* */
VirtQueueElement elem;
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 1b2c12ad94..015f5b5276 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1104,7 +1104,7 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env,
}
static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu,
- int sipi_vector)
+ uint8_t sipi_vector)
{
CPUState *cs = CPU(cpu);
CPUX86State *env = &cpu->env;
diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c
index af5c1c6830..c98eeb4351 100644
--- a/target-i386/seg_helper.c
+++ b/target-i386/seg_helper.c
@@ -883,32 +883,23 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int,
}
if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
/* to inner privilege */
- if (ist != 0) {
- esp = get_rsp_from_tss(env, ist + 3);
- } else {
- esp = get_rsp_from_tss(env, dpl);
- }
- esp &= ~0xfLL; /* align stack */
- ss = 0;
new_stack = 1;
+ esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl);
+ ss = 0;
} else if ((e2 & DESC_C_MASK) || dpl == cpl) {
/* to same privilege */
if (env->eflags & VM_MASK) {
raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
}
new_stack = 0;
- if (ist != 0) {
- esp = get_rsp_from_tss(env, ist + 3);
- } else {
- esp = env->regs[R_ESP];
- }
- esp &= ~0xfLL; /* align stack */
+ esp = env->regs[R_ESP];
dpl = cpl;
} else {
raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
new_stack = 0; /* avoid warning */
esp = 0; /* avoid warning */
}
+ esp &= ~0xfLL; /* align stack */
PUSHQ(esp, env->segs[R_SS].selector);
PUSHQ(esp, env->regs[R_ESP]);