aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi.c172
-rw-r--r--hw/acpi.h93
-rw-r--r--hw/acpi_piix4.c70
-rw-r--r--hw/baum.c7
-rw-r--r--hw/baum.h2
-rw-r--r--hw/blizzard.c8
-rw-r--r--hw/esp.c3
-rw-r--r--hw/g364fb.c2
-rw-r--r--hw/ide/ahci.c82
-rw-r--r--hw/ide/atapi.c7
-rw-r--r--hw/ide/core.c16
-rw-r--r--hw/jazz_led.c7
-rw-r--r--hw/lsi53c895a.c2
-rw-r--r--hw/mc146818rtc.c13
-rw-r--r--hw/mips_malta.c2
-rw-r--r--hw/msmouse.c5
-rw-r--r--hw/msmouse.h2
-rw-r--r--hw/ne2000.c9
-rw-r--r--hw/omap_lcdc.c7
-rw-r--r--hw/pc.c11
-rw-r--r--hw/pc.h3
-rw-r--r--hw/pc_piix.c8
-rw-r--r--hw/pci.h1
-rw-r--r--hw/pcnet-pci.c9
-rw-r--r--hw/ps2.c6
-rw-r--r--hw/pxa2xx_lcd.c7
-rw-r--r--hw/qxl.c4
-rw-r--r--hw/s390-virtio-bus.c33
-rw-r--r--hw/s390-virtio-bus.h2
-rw-r--r--hw/scsi-bus.c159
-rw-r--r--hw/scsi-disk.c151
-rw-r--r--hw/scsi-generic.c25
-rw-r--r--hw/scsi.h22
-rw-r--r--hw/serial.c6
-rw-r--r--hw/spapr_vscsi.c2
-rw-r--r--hw/tcx.c8
-rw-r--r--hw/usb-msd.c2
-rw-r--r--hw/vga.c46
-rw-r--r--hw/virtio-blk.c6
-rw-r--r--hw/virtio-pci.c56
-rw-r--r--hw/virtio-pci.h2
-rw-r--r--hw/virtio-scsi.c617
-rw-r--r--hw/virtio-scsi.h36
-rw-r--r--hw/virtio.h3
-rw-r--r--hw/vmware_vga.c4
-rw-r--r--hw/vt82c686.c42
46 files changed, 1368 insertions, 412 deletions
diff --git a/hw/acpi.c b/hw/acpi.c
index 79b179b..5d521e5 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -248,64 +248,97 @@ int acpi_table_add(const char *t)
}
+static void acpi_notify_wakeup(Notifier *notifier, void *data)
+{
+ ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
+ WakeupReason *reason = data;
+
+ switch (*reason) {
+ case QEMU_WAKEUP_REASON_RTC:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_PMTIMER:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_OTHER:
+ default:
+ /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
+ Pretend that resume was caused by power button */
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
+ break;
+ }
+}
+
/* ACPI PM1a EVT */
-uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time)
+uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
{
int64_t d = acpi_pm_tmr_get_clock();
- if (d >= overflow_time) {
- pm1->sts |= ACPI_BITMASK_TIMER_STATUS;
+ if (d >= ar->tmr.overflow_time) {
+ ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
}
- return pm1->sts;
+ return ar->pm1.evt.sts;
}
-void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val)
+void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
{
- uint16_t pm1_sts = acpi_pm1_evt_get_sts(pm1, tmr->overflow_time);
+ uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
/* if TMRSTS is reset, then compute the new overflow time */
- acpi_pm_tmr_calc_overflow_time(tmr);
+ acpi_pm_tmr_calc_overflow_time(ar);
}
- pm1->sts &= ~val;
+ ar->pm1.evt.sts &= ~val;
}
-void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr)
+void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
{
- if (!pm1) {
- qemu_system_shutdown_request();
- } else if (pm1->en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
- pm1->sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
- tmr->update_sci(tmr);
+ ar->pm1.evt.en = val;
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
+ val & ACPI_BITMASK_RT_CLOCK_ENABLE);
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
+ val & ACPI_BITMASK_TIMER_ENABLE);
+}
+
+void acpi_pm1_evt_power_down(ACPIREGS *ar)
+{
+ if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
+ ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
+ ar->tmr.update_sci(ar);
}
}
-void acpi_pm1_evt_reset(ACPIPM1EVT *pm1)
+void acpi_pm1_evt_reset(ACPIREGS *ar)
{
- pm1->sts = 0;
- pm1->en = 0;
+ ar->pm1.evt.sts = 0;
+ ar->pm1.evt.en = 0;
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
}
/* ACPI PM_TMR */
-void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable)
+void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
{
int64_t expire_time;
/* schedule a timer interruption if needed */
if (enable) {
- expire_time = muldiv64(tmr->overflow_time, get_ticks_per_sec(),
+ expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(),
PM_TIMER_FREQUENCY);
- qemu_mod_timer(tmr->timer, expire_time);
+ qemu_mod_timer(ar->tmr.timer, expire_time);
} else {
- qemu_del_timer(tmr->timer);
+ qemu_del_timer(ar->tmr.timer);
}
}
-void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr)
+void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
{
int64_t d = acpi_pm_tmr_get_clock();
- tmr->overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+ ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
}
-uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr)
+uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
{
uint32_t d = acpi_pm_tmr_get_clock();
return d & 0xffffff;
@@ -313,31 +346,33 @@ uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr)
static void acpi_pm_tmr_timer(void *opaque)
{
- ACPIPMTimer *tmr = opaque;
- tmr->update_sci(tmr);
+ ACPIREGS *ar = opaque;
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER);
+ ar->tmr.update_sci(ar);
}
-void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci)
+void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci)
{
- tmr->update_sci = update_sci;
- tmr->timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, tmr);
+ ar->tmr.update_sci = update_sci;
+ ar->tmr.timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, ar);
}
-void acpi_pm_tmr_reset(ACPIPMTimer *tmr)
+void acpi_pm_tmr_reset(ACPIREGS *ar)
{
- tmr->overflow_time = 0;
- qemu_del_timer(tmr->timer);
+ ar->tmr.overflow_time = 0;
+ qemu_del_timer(ar->tmr.timer);
}
/* ACPI PM1aCNT */
-void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3)
+void acpi_pm1_cnt_init(ACPIREGS *ar)
{
- pm1_cnt->cmos_s3 = cmos_s3;
+ ar->wakeup.notify = acpi_notify_wakeup;
+ qemu_register_wakeup_notifier(&ar->wakeup);
}
-void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val)
+void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
{
- pm1_cnt->cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+ ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
if (val & ACPI_BITMASK_SLEEP_ENABLE) {
/* change suspend type */
@@ -347,64 +382,57 @@ void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val)
qemu_system_shutdown_request();
break;
case 1:
- /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
- Pretend that resume was caused by power button */
- pm1a->sts |=
- (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
- qemu_system_reset_request();
- qemu_irq_raise(pm1_cnt->cmos_s3);
+ qemu_system_suspend_request();
+ break;
default:
break;
}
}
}
-void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt,
+void acpi_pm1_cnt_update(ACPIREGS *ar,
bool sci_enable, bool sci_disable)
{
/* ACPI specs 3.0, 4.7.2.5 */
if (sci_enable) {
- pm1_cnt->cnt |= ACPI_BITMASK_SCI_ENABLE;
+ ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
} else if (sci_disable) {
- pm1_cnt->cnt &= ~ACPI_BITMASK_SCI_ENABLE;
+ ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
}
}
-void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt)
+void acpi_pm1_cnt_reset(ACPIREGS *ar)
{
- pm1_cnt->cnt = 0;
- if (pm1_cnt->cmos_s3) {
- qemu_irq_lower(pm1_cnt->cmos_s3);
- }
+ ar->pm1.cnt.cnt = 0;
}
/* ACPI GPE */
-void acpi_gpe_init(ACPIGPE *gpe, uint8_t len)
+void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
{
- gpe->len = len;
- gpe->sts = g_malloc0(len / 2);
- gpe->en = g_malloc0(len / 2);
+ ar->gpe.len = len;
+ ar->gpe.sts = g_malloc0(len / 2);
+ ar->gpe.en = g_malloc0(len / 2);
}
-void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk)
+void acpi_gpe_blk(ACPIREGS *ar, uint32_t blk)
{
- gpe->blk = blk;
+ ar->gpe.blk = blk;
}
-void acpi_gpe_reset(ACPIGPE *gpe)
+void acpi_gpe_reset(ACPIREGS *ar)
{
- memset(gpe->sts, 0, gpe->len / 2);
- memset(gpe->en, 0, gpe->len / 2);
+ memset(ar->gpe.sts, 0, ar->gpe.len / 2);
+ memset(ar->gpe.en, 0, ar->gpe.len / 2);
}
-static uint8_t *acpi_gpe_ioport_get_ptr(ACPIGPE *gpe, uint32_t addr)
+static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
{
uint8_t *cur = NULL;
- if (addr < gpe->len / 2) {
- cur = gpe->sts + addr;
- } else if (addr < gpe->len) {
- cur = gpe->en + addr - gpe->len / 2;
+ if (addr < ar->gpe.len / 2) {
+ cur = ar->gpe.sts + addr;
+ } else if (addr < ar->gpe.len) {
+ cur = ar->gpe.en + addr - ar->gpe.len / 2;
} else {
abort();
}
@@ -412,16 +440,16 @@ static uint8_t *acpi_gpe_ioport_get_ptr(ACPIGPE *gpe, uint32_t addr)
return cur;
}
-void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val)
+void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
{
uint8_t *cur;
- addr -= gpe->blk;
- cur = acpi_gpe_ioport_get_ptr(gpe, addr);
- if (addr < gpe->len / 2) {
+ addr -= ar->gpe.blk;
+ cur = acpi_gpe_ioport_get_ptr(ar, addr);
+ if (addr < ar->gpe.len / 2) {
/* GPE_STS */
*cur = (*cur) & ~val;
- } else if (addr < gpe->len) {
+ } else if (addr < ar->gpe.len) {
/* GPE_EN */
*cur = val;
} else {
@@ -429,13 +457,13 @@ void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val)
}
}
-uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr)
+uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
{
uint8_t *cur;
uint32_t val;
- addr -= gpe->blk;
- cur = acpi_gpe_ioport_get_ptr(gpe, addr);
+ addr -= ar->gpe.blk;
+ cur = acpi_gpe_ioport_get_ptr(ar, addr);
val = 0;
if (cur != NULL) {
val = *cur;
diff --git a/hw/acpi.h b/hw/acpi.h
index c141e65..fe8cdb4 100644
--- a/hw/acpi.h
+++ b/hw/acpi.h
@@ -73,11 +73,14 @@
/* PM2_CNT */
#define ACPI_BITMASK_ARB_DISABLE 0x0001
-/* PM_TMR */
-struct ACPIPMTimer;
+/* structs */
typedef struct ACPIPMTimer ACPIPMTimer;
+typedef struct ACPIPM1EVT ACPIPM1EVT;
+typedef struct ACPIPM1CNT ACPIPM1CNT;
+typedef struct ACPIGPE ACPIGPE;
+typedef struct ACPIREGS ACPIREGS;
-typedef void (*acpi_update_sci_fn)(ACPIPMTimer *tmr);
+typedef void (*acpi_update_sci_fn)(ACPIREGS *ar);
struct ACPIPMTimer {
QEMUTimer *timer;
@@ -86,47 +89,15 @@ struct ACPIPMTimer {
acpi_update_sci_fn update_sci;
};
-void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable);
-void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr);
-uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr);
-void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci);
-void acpi_pm_tmr_reset(ACPIPMTimer *tmr);
-
-#include "qemu-timer.h"
-static inline int64_t acpi_pm_tmr_get_clock(void)
-{
- return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY,
- get_ticks_per_sec());
-}
-
-/* PM1a_EVT: piix and ich9 don't implement PM1b. */
-struct ACPIPM1EVT
-{
+struct ACPIPM1EVT {
uint16_t sts;
uint16_t en;
};
-typedef struct ACPIPM1EVT ACPIPM1EVT;
-uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time);
-void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val);
-void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr);
-void acpi_pm1_evt_reset(ACPIPM1EVT *pm1);
-
-/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */
struct ACPIPM1CNT {
uint16_t cnt;
-
- qemu_irq cmos_s3;
};
-typedef struct ACPIPM1CNT ACPIPM1CNT;
-
-void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3);
-void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val);
-void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt,
- bool sci_enable, bool sci_disable);
-void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt);
-/* GPE0 */
struct ACPIGPE {
uint32_t blk;
uint8_t len;
@@ -134,13 +105,51 @@ struct ACPIGPE {
uint8_t *sts;
uint8_t *en;
};
-typedef struct ACPIGPE ACPIGPE;
-void acpi_gpe_init(ACPIGPE *gpe, uint8_t len);
-void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk);
-void acpi_gpe_reset(ACPIGPE *gpe);
+struct ACPIREGS {
+ ACPIPMTimer tmr;
+ ACPIGPE gpe;
+ struct {
+ ACPIPM1EVT evt;
+ ACPIPM1CNT cnt;
+ } pm1;
+ Notifier wakeup;
+};
+
+/* PM_TMR */
+void acpi_pm_tmr_update(ACPIREGS *ar, bool enable);
+void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar);
+uint32_t acpi_pm_tmr_get(ACPIREGS *ar);
+void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci);
+void acpi_pm_tmr_reset(ACPIREGS *ar);
+
+#include "qemu-timer.h"
+static inline int64_t acpi_pm_tmr_get_clock(void)
+{
+ return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY,
+ get_ticks_per_sec());
+}
+
+/* PM1a_EVT: piix and ich9 don't implement PM1b. */
+uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar);
+void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_evt_power_down(ACPIREGS *ar);
+void acpi_pm1_evt_reset(ACPIREGS *ar);
+
+/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */
+void acpi_pm1_cnt_init(ACPIREGS *ar);
+void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val);
+void acpi_pm1_cnt_update(ACPIREGS *ar,
+ bool sci_enable, bool sci_disable);
+void acpi_pm1_cnt_reset(ACPIREGS *ar);
+
+/* GPE0 */
+void acpi_gpe_init(ACPIREGS *ar, uint8_t len);
+void acpi_gpe_blk(ACPIREGS *ar, uint32_t blk);
+void acpi_gpe_reset(ACPIREGS *ar);
-void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val);
-uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr);
+void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val);
+uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr);
#endif /* !QEMU_HW_ACPI_H */
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index d959f49..797ed24 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -54,13 +54,10 @@ struct pci_status {
typedef struct PIIX4PMState {
PCIDevice dev;
IORange ioport;
- ACPIPM1EVT pm1a;
- ACPIPM1CNT pm1_cnt;
+ ACPIREGS ar;
APMState apm;
- ACPIPMTimer tmr;
-
PMSMBus smb;
uint32_t smb_io_base;
@@ -70,7 +67,6 @@ typedef struct PIIX4PMState {
Notifier machine_ready;
/* for pci hotplug */
- ACPIGPE gpe;
struct pci_status pci0_status;
uint32_t pci0_hotplug_enable;
} PIIX4PMState;
@@ -84,23 +80,24 @@ static void pm_update_sci(PIIX4PMState *s)
{
int sci_level, pmsts;
- pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
- sci_level = (((pmsts & s->pm1a.en) &
+ pmsts = acpi_pm1_evt_get_sts(&s->ar);
+ sci_level = (((pmsts & s->ar.pm1.evt.en) &
(ACPI_BITMASK_RT_CLOCK_ENABLE |
ACPI_BITMASK_POWER_BUTTON_ENABLE |
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
- (((s->gpe.sts[0] & s->gpe.en[0]) & PIIX4_PCI_HOTPLUG_STATUS) != 0);
+ (((s->ar.gpe.sts[0] & s->ar.gpe.en[0])
+ & PIIX4_PCI_HOTPLUG_STATUS) != 0);
qemu_set_irq(s->irq, sci_level);
/* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
+ acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
!(pmsts & ACPI_BITMASK_TIMER_STATUS));
}
-static void pm_tmr_timer(ACPIPMTimer *tmr)
+static void pm_tmr_timer(ACPIREGS *ar)
{
- PIIX4PMState *s = container_of(tmr, PIIX4PMState, tmr);
+ PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
pm_update_sci(s);
}
@@ -116,15 +113,15 @@ static void pm_ioport_write(IORange *ioport, uint64_t addr, unsigned width,
switch(addr) {
case 0x00:
- acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val);
+ acpi_pm1_evt_write_sts(&s->ar, val);
pm_update_sci(s);
break;
case 0x02:
- s->pm1a.en = val;
+ acpi_pm1_evt_write_en(&s->ar, val);
pm_update_sci(s);
break;
case 0x04:
- acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val);
+ acpi_pm1_cnt_write(&s->ar, val);
break;
default:
break;
@@ -141,16 +138,16 @@ static void pm_ioport_read(IORange *ioport, uint64_t addr, unsigned width,
switch(addr) {
case 0x00:
- val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
+ val = acpi_pm1_evt_get_sts(&s->ar);
break;
case 0x02:
- val = s->pm1a.en;
+ val = s->ar.pm1.evt.en;
break;
case 0x04:
- val = s->pm1_cnt.cnt;
+ val = s->ar.pm1.cnt.cnt;
break;
case 0x08:
- val = acpi_pm_tmr_get(&s->tmr);
+ val = acpi_pm_tmr_get(&s->ar);
break;
default:
val = 0;
@@ -170,7 +167,7 @@ static void apm_ctrl_changed(uint32_t val, void *arg)
PIIX4PMState *s = arg;
/* ACPI specs 3.0, 4.7.2.5 */
- acpi_pm1_cnt_update(&s->pm1_cnt, val == ACPI_ENABLE, val == ACPI_DISABLE);
+ acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE);
if (s->dev.config[0x5b] & (1 << 1)) {
if (s->smi_irq) {
@@ -258,13 +255,13 @@ static const VMStateDescription vmstate_acpi = {
.post_load = vmstate_acpi_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, PIIX4PMState),
- VMSTATE_UINT16(pm1a.sts, PIIX4PMState),
- VMSTATE_UINT16(pm1a.en, PIIX4PMState),
- VMSTATE_UINT16(pm1_cnt.cnt, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
+ VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
- VMSTATE_TIMER(tmr.timer, PIIX4PMState),
- VMSTATE_INT64(tmr.overflow_time, PIIX4PMState),
- VMSTATE_STRUCT(gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
+ VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
+ VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
+ VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
struct pci_status),
VMSTATE_END_OF_LIST()
@@ -310,10 +307,9 @@ static void piix4_reset(void *opaque)
static void piix4_powerdown(void *opaque, int irq, int power_failing)
{
PIIX4PMState *s = opaque;
- ACPIPM1EVT *pm1a = s? &s->pm1a: NULL;
- ACPIPMTimer *tmr = s? &s->tmr: NULL;
- acpi_pm1_evt_power_down(pm1a, tmr);
+ assert(s != NULL);
+ acpi_pm1_evt_power_down(&s->ar);
}
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
@@ -361,8 +357,8 @@ static int piix4_pm_initfn(PCIDevice *dev)
register_ioport_write(s->smb_io_base, 64, 1, smb_ioport_writeb, &s->smb);
register_ioport_read(s->smb_io_base, 64, 1, smb_ioport_readb, &s->smb);
- acpi_pm_tmr_init(&s->tmr, pm_tmr_timer);
- acpi_gpe_init(&s->gpe, GPE_LEN);
+ acpi_pm_tmr_init(&s->ar, pm_tmr_timer);
+ acpi_gpe_init(&s->ar, GPE_LEN);
qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1);
@@ -376,7 +372,7 @@ static int piix4_pm_initfn(PCIDevice *dev)
}
i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ qemu_irq sci_irq, qemu_irq smi_irq,
int kvm_enabled)
{
PCIDevice *dev;
@@ -387,7 +383,7 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
s = DO_UPCAST(PIIX4PMState, dev, dev);
s->irq = sci_irq;
- acpi_pm1_cnt_init(&s->pm1_cnt, cmos_s3);
+ acpi_pm1_cnt_init(&s->ar);
s->smi_irq = smi_irq;
s->kvm_enabled = kvm_enabled;
@@ -436,7 +432,7 @@ type_init(piix4_pm_register_types)
static uint32_t gpe_readb(void *opaque, uint32_t addr)
{
PIIX4PMState *s = opaque;
- uint32_t val = acpi_gpe_ioport_readb(&s->gpe, addr);
+ uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
return val;
@@ -446,7 +442,7 @@ static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
{
PIIX4PMState *s = opaque;
- acpi_gpe_ioport_writeb(&s->gpe, addr, val);
+ acpi_gpe_ioport_writeb(&s->ar, addr, val);
pm_update_sci(s);
PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
@@ -531,7 +527,7 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
register_ioport_write(GPE_BASE, GPE_LEN, 1, gpe_writeb, s);
register_ioport_read(GPE_BASE, GPE_LEN, 1, gpe_readb, s);
- acpi_gpe_blk(&s->gpe, GPE_BASE);
+ acpi_gpe_blk(&s->ar, GPE_BASE);
register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, pci0_status);
register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, pci0_status);
@@ -547,13 +543,13 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
static void enable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.up |= (1 << slot);
}
static void disable_device(PIIX4PMState *s, int slot)
{
- s->gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
s->pci0_status.down |= (1 << slot);
}
diff --git a/hw/baum.c b/hw/baum.c
index 86d780a..3e94f84 100644
--- a/hw/baum.c
+++ b/hw/baum.c
@@ -562,7 +562,7 @@ static void baum_close(struct CharDriverState *chr)
g_free(baum);
}
-int chr_baum_init(QemuOpts *opts, CharDriverState **_chr)
+CharDriverState *chr_baum_init(QemuOpts *opts)
{
BaumDriverState *baum;
CharDriverState *chr;
@@ -614,8 +614,7 @@ int chr_baum_init(QemuOpts *opts, CharDriverState **_chr)
qemu_chr_generic_open(chr);
- *_chr = chr;
- return 0;
+ return chr;
fail:
qemu_free_timer(baum->cellCount_timer);
@@ -624,5 +623,5 @@ fail_handle:
g_free(handle);
g_free(chr);
g_free(baum);
- return -EIO;
+ return NULL;
}
diff --git a/hw/baum.h b/hw/baum.h
index 3f28cc3..8af710f 100644
--- a/hw/baum.h
+++ b/hw/baum.h
@@ -23,4 +23,4 @@
*/
/* char device */
-int chr_baum_init(QemuOpts *opts, CharDriverState **_chr);
+CharDriverState *chr_baum_init(QemuOpts *opts);
diff --git a/hw/blizzard.c b/hw/blizzard.c
index b2c1b22..c7d844d 100644
--- a/hw/blizzard.c
+++ b/hw/blizzard.c
@@ -932,10 +932,14 @@ static void blizzard_update_display(void *opaque)
s->my[1] = 0;
}
-static void blizzard_screen_dump(void *opaque, const char *filename) {
+static void blizzard_screen_dump(void *opaque, const char *filename,
+ bool cswitch)
+{
BlizzardState *s = (BlizzardState *) opaque;
- blizzard_update_display(opaque);
+ if (cswitch) {
+ blizzard_update_display(opaque);
+ }
if (s && ds_get_data(s->state))
ppm_save(filename, s->state->surface);
}
diff --git a/hw/esp.c b/hw/esp.c
index 2dda8e3..8d73e56 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -390,7 +390,8 @@ static void esp_do_dma(ESPState *s)
esp_dma_done(s);
}
-static void esp_command_complete(SCSIRequest *req, uint32_t status)
+static void esp_command_complete(SCSIRequest *req, uint32_t status,
+ size_t resid)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
diff --git a/hw/g364fb.c b/hw/g364fb.c
index 9c63bdd..3a0b68f 100644
--- a/hw/g364fb.c
+++ b/hw/g364fb.c
@@ -289,7 +289,7 @@ static void g364fb_reset(G364State *s)
g364fb_invalidate_display(s);
}
-static void g364fb_screen_dump(void *opaque, const char *filename)
+static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch)
{
G364State *s = opaque;
int y, x;
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index b515f41..041ce1e 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -428,55 +428,6 @@ static void ahci_reg_init(AHCIState *s)
}
}
-static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len,
- QEMUSGList *sglist)
-{
- uint32_t i = 0;
- uint32_t total = 0, once;
- ScatterGatherEntry *cur_prd;
- uint32_t sgcount;
-
- cur_prd = sglist->sg;
- sgcount = sglist->nsg;
- for (i = 0; len && sgcount; i++) {
- once = MIN(cur_prd->len, len);
- cpu_physical_memory_read(cur_prd->base, buffer, once);
- cur_prd++;
- sgcount--;
- len -= once;
- buffer += once;
- total += once;
- }
-
- return total;
-}
-
-static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len,
- QEMUSGList *sglist)
-{
- uint32_t i = 0;
- uint32_t total = 0, once;
- ScatterGatherEntry *cur_prd;
- uint32_t sgcount;
-
- DPRINTF(-1, "total: 0x%x bytes\n", len);
-
- cur_prd = sglist->sg;
- sgcount = sglist->nsg;
- for (i = 0; len && sgcount; i++) {
- once = MIN(cur_prd->len, len);
- DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base);
- cpu_physical_memory_write(cur_prd->base, buffer, once);
- cur_prd++;
- sgcount--;
- len -= once;
- buffer += once;
- total += once;
- }
-
- return total;
-}
-
static void check_cmd(AHCIState *s, int port)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
@@ -802,9 +753,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
DPRINTF(port, "tag %d aio read %"PRId64"\n",
ncq_tfs->tag, ncq_tfs->lba);
- bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
- (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
- BDRV_ACCT_READ);
+ dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BDRV_ACCT_READ);
ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
&ncq_tfs->sglist, ncq_tfs->lba,
ncq_cb, ncq_tfs);
@@ -816,9 +766,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
DPRINTF(port, "tag %d aio write %"PRId64"\n",
ncq_tfs->tag, ncq_tfs->lba);
- bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
- (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
- BDRV_ACCT_WRITE);
+ dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BDRV_ACCT_WRITE);
ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
&ncq_tfs->sglist, ncq_tfs->lba,
ncq_cb, ncq_tfs);
@@ -1023,12 +972,12 @@ static int ahci_start_transfer(IDEDMA *dma)
is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata",
has_sglist ? "" : "o");
- if (is_write && has_sglist && (s->data_ptr < s->data_end)) {
- read_from_sglist(s->data_ptr, size, &s->sg);
- }
-
- if (!is_write && has_sglist && (s->data_ptr < s->data_end)) {
- write_to_sglist(s->data_ptr, size, &s->sg);
+ if (has_sglist && size) {
+ if (is_write) {
+ dma_buf_write(s->data_ptr, size, &s->sg);
+ } else {
+ dma_buf_read(s->data_ptr, size, &s->sg);
+ }
}
/* update number of transferred bytes */
@@ -1067,14 +1016,9 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
- int i;
ahci_populate_sglist(ad, &s->sg);
-
- s->io_buffer_size = 0;
- for (i = 0; i < s->sg.nsg; i++) {
- s->io_buffer_size += s->sg.sg[i].len;
- }
+ s->io_buffer_size = s->sg.size;
DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
return s->io_buffer_size != 0;
@@ -1092,9 +1036,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
}
if (is_write) {
- write_to_sglist(p, l, &s->sg);
+ dma_buf_read(p, l, &s->sg);
} else {
- read_from_sglist(p, l, &s->sg);
+ dma_buf_write(p, l, &s->sg);
}
/* update number of transferred bytes */
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 0adb27b..5919cf5 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -883,8 +883,11 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
return;
}
- bdrv_eject(s->bs, !start);
- s->tray_open = !start;
+
+ if (s->tray_open != !start) {
+ bdrv_eject(s->bs, !start);
+ s->tray_open = !start;
+ }
}
ide_atapi_cmd_ok(s);
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 56b219b..ce570a7 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -519,7 +519,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
@@ -527,7 +527,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
|| action == BLOCK_ERR_STOP_ANY) {
s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
s->bus->error_status = op;
- bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->bs, error);
} else {
@@ -537,7 +537,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
} else {
ide_rw_error(s);
}
- bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
@@ -2077,15 +2077,6 @@ static bool ide_drive_pio_state_needed(void *opaque)
|| (s->bus->error_status & BM_STATUS_PIO_RETRY);
}
-static int ide_tray_state_post_load(void *opaque, int version_id)
-{
- IDEState *s = opaque;
-
- bdrv_eject(s->bs, s->tray_open);
- bdrv_lock_medium(s->bs, s->tray_locked);
- return 0;
-}
-
static bool ide_tray_state_needed(void *opaque)
{
IDEState *s = opaque;
@@ -2125,7 +2116,6 @@ static const VMStateDescription vmstate_ide_tray_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
- .post_load = ide_tray_state_post_load,
.fields = (VMStateField[]) {
VMSTATE_BOOL(tray_open, IDEState),
VMSTATE_BOOL(tray_locked, IDEState),
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
index 5d8040b..6486523 100644
--- a/hw/jazz_led.c
+++ b/hw/jazz_led.c
@@ -205,11 +205,6 @@ static void jazz_led_invalidate_display(void *opaque)
s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
}
-static void jazz_led_screen_dump(void *opaque, const char *filename)
-{
- printf("jazz_led_screen_dump() not implemented\n");
-}
-
static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
{
LedState *s = opaque;
@@ -255,7 +250,7 @@ static int jazz_led_init(SysBusDevice *dev)
s->ds = graphic_console_init(jazz_led_update_display,
jazz_led_invalidate_display,
- jazz_led_screen_dump,
+ NULL,
jazz_led_text_update, s);
return 0;
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 0acd1d0..edc09b7 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -699,7 +699,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
}
/* Callback to indicate that the SCSI layer has completed a command. */
-static void lsi_command_complete(SCSIRequest *req, uint32_t status)
+static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 6c1ad38..a46fdfc 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -102,6 +102,7 @@ typedef struct RTCState {
QEMUTimer *second_timer2;
Notifier clock_reset_notifier;
LostTickPolicy lost_tick_policy;
+ Notifier suspend_notifier;
} RTCState;
static void rtc_set_time(RTCState *s);
@@ -436,6 +437,7 @@ static void rtc_update_second2(void *opaque)
s->cmos_data[RTC_REG_C] |= REG_C_AF;
if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
qemu_irq_raise(s->irq);
s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
}
@@ -596,6 +598,14 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data)
#endif
}
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ BIOS will read it and start S3 resume at POST Entry */
+static void rtc_notify_suspend(Notifier *notifier, void *data)
+{
+ RTCState *s = container_of(notifier, RTCState, suspend_notifier);
+ rtc_set_memory(&s->dev, 0xF, 0xFE);
+}
+
static void rtc_reset(void *opaque)
{
RTCState *s = opaque;
@@ -676,6 +686,9 @@ static int rtc_initfn(ISADevice *dev)
s->clock_reset_notifier.notify = rtc_notify_clock_reset;
qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
+ s->suspend_notifier.notify = rtc_notify_suspend;
+ qemu_register_suspend_notifier(&s->suspend_notifier);
+
s->next_second_time =
qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
qemu_mod_timer(s->second_timer2, s->next_second_time);
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index 86a5fbb..b1563ed 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -967,7 +967,7 @@ void mips_malta_init (ram_addr_t ram_size,
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
usb_uhci_piix4_init(pci_bus, piix4_devfn + 2);
smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
- isa_get_irq(NULL, 9), NULL, NULL, 0);
+ isa_get_irq(NULL, 9), NULL, 0);
/* TODO: Populate SPD eeprom data. */
smbus_eeprom_init(smbus, 8, NULL, 0);
pit = pit_init(isa_bus, 0x40, 0, NULL);
diff --git a/hw/msmouse.c b/hw/msmouse.c
index c3b57ea..9c492a4 100644
--- a/hw/msmouse.c
+++ b/hw/msmouse.c
@@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr)
g_free (chr);
}
-int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr)
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
{
CharDriverState *chr;
@@ -74,6 +74,5 @@ int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr)
qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
- *_chr = chr;
- return 0;
+ return chr;
}
diff --git a/hw/msmouse.h b/hw/msmouse.h
index 8b853b3..456cb21 100644
--- a/hw/msmouse.h
+++ b/hw/msmouse.h
@@ -1,2 +1,2 @@
/* msmouse.c */
-int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr);
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts);
diff --git a/hw/ne2000.c b/hw/ne2000.c
index bb84fd1..71452e1 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -763,14 +763,6 @@ static int pci_ne2000_init(PCIDevice *pci_dev)
object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
- if (!pci_dev->qdev.hotplugged) {
- static int loaded = 0;
- if (!loaded) {
- rom_add_option("pxe-ne2k_pci.rom", -1);
- loaded = 1;
- }
- }
-
add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
return 0;
@@ -798,6 +790,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data)
k->init = pci_ne2000_init;
k->exit = pci_ne2000_exit;
+ k->romfile = "pxe-ne2k_pci.rom",
k->vendor_id = PCI_VENDOR_ID_REALTEK;
k->device_id = PCI_DEVICE_ID_REALTEK_8029;
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c
index f265306..f172093 100644
--- a/hw/omap_lcdc.c
+++ b/hw/omap_lcdc.c
@@ -264,9 +264,12 @@ static int ppm_save(const char *filename, uint8_t *data,
return 0;
}
-static void omap_screen_dump(void *opaque, const char *filename) {
+static void omap_screen_dump(void *opaque, const char *filename, bool cswitch)
+{
struct omap_lcd_panel_s *omap_lcd = opaque;
- omap_update_display(opaque);
+ if (cswitch) {
+ omap_update_display(opaque);
+ }
if (omap_lcd && ds_get_data(omap_lcd->state))
ppm_save(filename, ds_get_data(omap_lcd->state),
omap_lcd->width, omap_lcd->height,
diff --git a/hw/pc.c b/hw/pc.c
index b9f4bc7..59a7f39 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -914,17 +914,6 @@ static DeviceState *apic_init(void *env, uint8_t apic_id)
return dev;
}
-/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
- BIOS will read it and start S3 resume at POST Entry */
-void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
-{
- ISADevice *s = opaque;
-
- if (level) {
- rtc_set_memory(s, 0xF, 0xFE);
- }
-}
-
void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
{
CPUState *s = opaque;
diff --git a/hw/pc.h b/hw/pc.h
index 71f7df3..74d3369 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -103,7 +103,6 @@ void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out);
extern int fd_bootchk;
void pc_register_ferr_irq(qemu_irq irq);
-void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
void pc_cpus_init(const char *cpu_model);
@@ -142,7 +141,7 @@ int acpi_table_add(const char *table_desc);
/* acpi_piix.c */
i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
- qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ qemu_irq sci_irq, qemu_irq smi_irq,
int kvm_enabled);
void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index fe7a729..5e11d15 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -139,7 +139,6 @@ static void pc_init1(MemoryRegion *system_memory,
qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
- qemu_irq *cmos_s3;
qemu_irq *smi_irq;
GSIState *gsi_state;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -291,15 +290,10 @@ static void pc_init1(MemoryRegion *system_memory,
if (pci_enabled && acpi_enabled) {
i2c_bus *smbus;
- if (!xen_enabled()) {
- cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
- } else {
- cmos_s3 = qemu_allocate_irqs(xen_cmos_set_s3_resume, rtc_state, 1);
- }
smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- gsi[9], *cmos_s3, *smi_irq,
+ gsi[9], *smi_irq,
kvm_enabled());
smbus_eeprom_init(smbus, 8, NULL, 0);
}
diff --git a/hw/pci.h b/hw/pci.h
index ee53f26..4f19fdb 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -75,6 +75,7 @@
#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
+#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
#define FMT_PCIBUS PRIx64
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index 3682609..34d73aa 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -330,14 +330,6 @@ static int pci_pcnet_init(PCIDevice *pci_dev)
s->phys_mem_write = pci_physical_memory_write;
s->dma_opaque = pci_dev;
- if (!pci_dev->qdev.hotplugged) {
- static int loaded = 0;
- if (!loaded) {
- rom_add_option("pxe-pcnet.rom", -1);
- loaded = 1;
- }
- }
-
return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
}
@@ -360,6 +352,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data)
k->init = pci_pcnet_init;
k->exit = pci_pcnet_uninit;
+ k->romfile = "pxe-pcnet.rom",
k->vendor_id = PCI_VENDOR_ID_AMD;
k->device_id = PCI_DEVICE_ID_AMD_LANCE;
k->revision = 0x10;
diff --git a/hw/ps2.c b/hw/ps2.c
index 1d9057b..b1a67bc 100644
--- a/hw/ps2.c
+++ b/hw/ps2.c
@@ -24,6 +24,7 @@
#include "hw.h"
#include "ps2.h"
#include "console.h"
+#include "sysemu.h"
/* debug PC keyboard */
//#define DEBUG_KBD
@@ -154,6 +155,7 @@ static void ps2_put_keycode(void *opaque, int keycode)
{
PS2KbdState *s = opaque;
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
/* XXX: add support for scancode set 1 */
if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
if (keycode & 0x80) {
@@ -368,6 +370,10 @@ static void ps2_mouse_event(void *opaque,
return;
s->mouse_buttons = buttons_state;
+ if (buttons_state) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+ }
+
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
for(;;) {
diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c
index 9495226..fcbdfb3 100644
--- a/hw/pxa2xx_lcd.c
+++ b/hw/pxa2xx_lcd.c
@@ -899,11 +899,6 @@ static void pxa2xx_invalidate_display(void *opaque)
s->invalidated = 1;
}
-static void pxa2xx_screen_dump(void *opaque, const char *filename)
-{
- /* TODO */
-}
-
static void pxa2xx_lcdc_orientation(void *opaque, int angle)
{
PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
@@ -1009,7 +1004,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
s->ds = graphic_console_init(pxa2xx_update_display,
pxa2xx_invalidate_display,
- pxa2xx_screen_dump, NULL, s);
+ NULL, NULL, s);
switch (ds_get_bits_per_pixel(s->ds)) {
case 0:
diff --git a/hw/qxl.c b/hw/qxl.c
index 87ad49a..f643667 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1436,7 +1436,7 @@ static void qxl_hw_invalidate(void *opaque)
vga->invalidate(vga);
}
-static void qxl_hw_screen_dump(void *opaque, const char *filename)
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
{
PCIQXLDevice *qxl = opaque;
VGACommonState *vga = &qxl->vga;
@@ -1448,7 +1448,7 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename)
ppm_save(filename, qxl->ssd.ds->surface);
break;
case QXL_MODE_VGA:
- vga->screen_dump(vga, filename);
+ vga->screen_dump(vga, filename, cswitch);
break;
default:
break;
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index 9d48056..c450e4b 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -169,6 +169,18 @@ static int s390_virtio_serial_init(VirtIOS390Device *dev)
return r;
}
+static int s390_virtio_scsi_init(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev;
+
+ vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
+ if (!vdev) {
+ return -1;
+ }
+
+ return s390_virtio_device_init(dev, vdev);
+}
+
static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
{
ram_addr_t token_off;
@@ -433,6 +445,26 @@ static TypeInfo virtio_s390_device_info = {
.abstract = true,
};
+static Property s390_virtio_scsi_properties[] = {
+ DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOS390Device, host_features, scsi),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
+
+ k->init = s390_virtio_scsi_init;
+ dc->props = s390_virtio_scsi_properties;
+}
+
+static TypeInfo s390_virtio_scsi = {
+ .name = "virtio-scsi-s390",
+ .parent = TYPE_VIRTIO_S390_DEVICE,
+ .instance_size = sizeof(VirtIOS390Device),
+ .class_init = s390_virtio_scsi_class_init,
+};
/***************** S390 Virtio Bus Bridge Device *******************/
/* Only required to have the virtio bus as child in the system bus */
@@ -465,6 +497,7 @@ static void s390_virtio_register_types(void)
type_register_static(&s390_virtio_serial);
type_register_static(&s390_virtio_blk);
type_register_static(&s390_virtio_net);
+ type_register_static(&s390_virtio_scsi);
type_register_static(&s390_virtio_bridge_info);
}
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index b5e59b7..0e60bc0 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -19,6 +19,7 @@
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
#define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */
#define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */
@@ -67,6 +68,7 @@ struct VirtIOS390Device {
uint32_t host_features;
virtio_serial_conf serial;
virtio_net_conf net;
+ VirtIOSCSIConf scsi;
};
typedef struct VirtIOS390Bus {
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index b3e97ce..2cb5a18 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -5,6 +5,7 @@
#include "qdev.h"
#include "blockdev.h"
#include "trace.h"
+#include "dma.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
@@ -86,6 +87,7 @@ static void scsi_dma_restart_bh(void *opaque)
scsi_req_continue(req);
break;
case SCSI_XFER_NONE:
+ assert(!req->sg);
scsi_req_dequeue(req);
scsi_req_enqueue(req);
break;
@@ -130,6 +132,10 @@ static int scsi_qdev_init(DeviceState *qdev)
error_report("bad scsi device id: %d", dev->id);
goto err;
}
+ if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
+ error_report("bad scsi device lun: %d", dev->lun);
+ goto err;
+ }
if (dev->id == -1) {
int id = -1;
@@ -138,8 +144,8 @@ static int scsi_qdev_init(DeviceState *qdev)
}
do {
d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
- } while (d && d->lun == dev->lun && id <= bus->info->max_target);
- if (id > bus->info->max_target) {
+ } while (d && d->lun == dev->lun && id < bus->info->max_target);
+ if (d && d->lun == dev->lun) {
error_report("no free target");
goto err;
}
@@ -149,14 +155,15 @@ static int scsi_qdev_init(DeviceState *qdev)
do {
d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
} while (d && d->lun == lun && lun < bus->info->max_lun);
- if (lun > bus->info->max_lun) {
+ if (d && d->lun == lun) {
error_report("no free lun");
goto err;
}
dev->lun = lun;
} else {
d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
- if (dev->lun == d->lun && dev != d) {
+ assert(d);
+ if (d->lun == dev->lun && dev != d) {
qdev_free(&d->qdev);
}
}
@@ -215,7 +222,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
int res = 0, unit;
loc_push_none(&loc);
- for (unit = 0; unit < bus->info->max_target; unit++) {
+ for (unit = 0; unit <= bus->info->max_target; unit++) {
dinfo = drive_get(IF_SCSI, bus->busnr, unit);
if (dinfo == NULL) {
continue;
@@ -378,7 +385,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
/* PAGE CODE == 0 */
if (r->req.cmd.xfer < 5) {
- return -1;
+ return false;
}
r->len = MIN(r->req.cmd.xfer, 36);
@@ -533,6 +540,8 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
}
req->cmd = cmd;
+ req->resid = req->cmd.xfer;
+
switch (buf[0]) {
case INQUIRY:
trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]);
@@ -643,15 +652,25 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
req->sense_len = 18;
}
-int32_t scsi_req_enqueue(SCSIRequest *req)
+static void scsi_req_enqueue_internal(SCSIRequest *req)
{
- int32_t rc;
-
assert(!req->enqueued);
scsi_req_ref(req);
+ if (req->bus->info->get_sg_list) {
+ req->sg = req->bus->info->get_sg_list(req);
+ } else {
+ req->sg = NULL;
+ }
req->enqueued = true;
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
+}
+int32_t scsi_req_enqueue(SCSIRequest *req)
+{
+ int32_t rc;
+
+ assert(!req->retry);
+ scsi_req_enqueue_internal(req);
scsi_req_ref(req);
rc = req->ops->send_command(req, req->cmd.buf);
scsi_req_unref(req);
@@ -1273,12 +1292,32 @@ void scsi_req_continue(SCSIRequest *req)
Once it completes, calling scsi_req_continue will restart I/O. */
void scsi_req_data(SCSIRequest *req, int len)
{
+ uint8_t *buf;
if (req->io_canceled) {
trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
- } else {
- trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ return;
+ }
+ trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ assert(req->cmd.mode != SCSI_XFER_NONE);
+ if (!req->sg) {
+ req->resid -= len;
req->bus->info->transfer_data(req, len);
+ return;
}
+
+ /* If the device calls scsi_req_data and the HBA specified a
+ * scatter/gather list, the transfer has to happen in a single
+ * step. */
+ assert(!req->dma_started);
+ req->dma_started = true;
+
+ buf = scsi_req_get_buf(req);
+ if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
+ req->resid = dma_buf_read(buf, len, req->sg);
+ } else {
+ req->resid = dma_buf_write(buf, len, req->sg);
+ }
+ scsi_req_continue(req);
}
void scsi_req_print(SCSIRequest *req)
@@ -1337,7 +1376,7 @@ void scsi_req_complete(SCSIRequest *req, int status)
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->info->complete(req, req->status);
+ req->bus->info->complete(req, req->status, req->resid);
scsi_req_unref(req);
}
@@ -1413,6 +1452,102 @@ SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
return target_dev;
}
+/* SCSI request list. For simplicity, pv points to the whole device */
+
+static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+ SCSIDevice *s = pv;
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+ SCSIRequest *req;
+
+ QTAILQ_FOREACH(req, &s->requests, next) {
+ assert(!req->io_canceled);
+ assert(req->status == -1);
+ assert(req->retry);
+ assert(req->enqueued);
+
+ qemu_put_sbyte(f, 1);
+ qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
+ qemu_put_be32s(f, &req->tag);
+ qemu_put_be32s(f, &req->lun);
+ if (bus->info->save_request) {
+ bus->info->save_request(f, req);
+ }
+ if (req->ops->save_request) {
+ req->ops->save_request(f, req);
+ }
+ }
+ qemu_put_sbyte(f, 0);
+}
+
+static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+ SCSIDevice *s = pv;
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+
+ while (qemu_get_sbyte(f)) {
+ uint8_t buf[SCSI_CMD_BUF_SIZE];
+ uint32_t tag;
+ uint32_t lun;
+ SCSIRequest *req;
+
+ qemu_get_buffer(f, buf, sizeof(buf));
+ qemu_get_be32s(f, &tag);
+ qemu_get_be32s(f, &lun);
+ req = scsi_req_new(s, tag, lun, buf, NULL);
+ if (bus->info->load_request) {
+ req->hba_private = bus->info->load_request(f, req);
+ }
+ if (req->ops->load_request) {
+ req->ops->load_request(f, req);
+ }
+
+ /* Just restart it later. */
+ req->retry = true;
+ scsi_req_enqueue_internal(req);
+
+ /* At this point, the request will be kept alive by the reference
+ * added by scsi_req_enqueue_internal, so we can release our reference.
+ * The HBA of course will add its own reference in the load_request
+ * callback if it needs to hold on the SCSIRequest.
+ */
+ scsi_req_unref(req);
+ }
+
+ return 0;
+}
+
+const VMStateInfo vmstate_info_scsi_requests = {
+ .name = "scsi-requests",
+ .get = get_scsi_requests,
+ .put = put_scsi_requests,
+};
+
+const VMStateDescription vmstate_scsi_device = {
+ .name = "SCSIDevice",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(unit_attention.key, SCSIDevice),
+ VMSTATE_UINT8(unit_attention.asc, SCSIDevice),
+ VMSTATE_UINT8(unit_attention.ascq, SCSIDevice),
+ VMSTATE_BOOL(sense_is_ua, SCSIDevice),
+ VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE),
+ VMSTATE_UINT32(sense_len, SCSIDevice),
+ {
+ .name = "requests",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0, /* ouch */
+ .info = &vmstate_info_scsi_requests,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void scsi_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index c12e3a6..add399e 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -38,6 +38,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "sysemu.h"
#include "blockdev.h"
#include "block_int.h"
+#include "dma.h"
#ifdef __linux
#include <scsi/sg.h>
@@ -110,12 +111,12 @@ static void scsi_cancel_io(SCSIRequest *req)
r->req.aiocb = NULL;
}
-static uint32_t scsi_init_iovec(SCSIDiskReq *r)
+static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
if (!r->iov.iov_base) {
- r->buflen = SCSI_DMA_BUF_SIZE;
+ r->buflen = size;
r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
}
r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
@@ -123,6 +124,56 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r)
return r->qiov.size / 512;
}
+static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_put_be64s(f, &r->sector);
+ qemu_put_be32s(f, &r->sector_count);
+ qemu_put_be32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+}
+
+static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_get_be64s(f, &r->sector);
+ qemu_get_be32s(f, &r->sector_count);
+ qemu_get_be32s(f, &r->buflen);
+ if (r->buflen) {
+ scsi_init_iovec(r, r->buflen);
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+ }
+
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+}
+
+static void scsi_dma_complete(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+
+ if (ret) {
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
+ }
+ }
+
+ r->sector += r->sector_count;
+ r->sector_count = 0;
+ scsi_req_complete(&r->req, GOOD);
+
+done:
+ scsi_req_unref(&r->req);
+}
+
static void scsi_read_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -213,10 +264,17 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
- n = scsi_init_iovec(r);
- bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
- r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
- scsi_read_complete, r);
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+ r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
+ }
}
/*
@@ -233,14 +291,14 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
|| action == BLOCK_ERR_STOP_ANY) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->qdev.conf.bs, error);
scsi_req_retry(&r->req);
@@ -259,7 +317,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
}
@@ -287,7 +345,7 @@ static void scsi_write_complete(void * opaque, int ret)
if (r->sector_count == 0) {
scsi_req_complete(&r->req, GOOD);
} else {
- scsi_init_iovec(r);
+ scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
scsi_req_data(&r->req, r->qiov.size);
}
@@ -315,18 +373,26 @@ static void scsi_write_data(SCSIRequest *req)
return;
}
- n = r->qiov.size / 512;
- if (n) {
- if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
- return;
- }
+ if (!r->req.sg && !r->qiov.size) {
+ /* Called for the first time. Ask the driver to send us more data. */
+ scsi_write_complete(r, 0);
+ return;
+ }
+ if (s->tray_open) {
+ scsi_write_complete(r, -ENOMEDIUM);
+ return;
+ }
+
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = r->qiov.size / 512;
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
- } else {
- /* Called for the first time. Ask the driver to send us more data. */
- scsi_write_complete(r, 0);
}
}
@@ -1050,8 +1116,11 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
: SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
return -1;
}
- bdrv_eject(s->qdev.conf.bs, !start);
- s->tray_open = !start;
+
+ if (s->tray_open != !start) {
+ bdrv_eject(s->qdev.conf.bs, !start);
+ s->tray_open = !start;
+ }
}
return 0;
}
@@ -1584,6 +1653,8 @@ static const SCSIReqOps scsi_disk_reqops = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .load_request = scsi_disk_load_request,
+ .save_request = scsi_disk_save_request,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
@@ -1686,6 +1757,15 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
+ /* If we are not using O_DIRECT, we might read stale data from the
+ * host cache if writes were made using other commands than these
+ * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
+ * O_DIRECT everything must go through SG_IO.
+ */
+ if (!(s->qdev.conf.bs->open_flags & BDRV_O_NOCACHE)) {
+ break;
+ }
+
/* MMC writing cannot be done via pread/pwrite, because it sometimes
* involves writing beyond the maximum LBA or to negative LBA (lead-in).
* And once you do these writes, reading from the block device is
@@ -1696,10 +1776,11 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
* seen, but performance usually isn't paramount on optical media. So,
* just make scsi-block operate the same as scsi-generic for them.
*/
- if (s->qdev.type != TYPE_ROM) {
- return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
- hba_private);
- }
+ if (s->qdev.type == TYPE_ROM) {
+ break;
+ }
+ return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
+ hba_private);
}
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
@@ -1718,6 +1799,22 @@ static Property scsi_hd_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static const VMStateDescription vmstate_scsi_disk_state = {
+ .name = "scsi-disk",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
+ VMSTATE_BOOL(media_changed, SCSIDiskState),
+ VMSTATE_BOOL(media_event, SCSIDiskState),
+ VMSTATE_BOOL(eject_request, SCSIDiskState),
+ VMSTATE_BOOL(tray_open, SCSIDiskState),
+ VMSTATE_BOOL(tray_locked, SCSIDiskState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1731,6 +1828,7 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI disk";
dc->reset = scsi_disk_reset;
dc->props = scsi_hd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_hd_info = {
@@ -1758,6 +1856,7 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI CD-ROM";
dc->reset = scsi_disk_reset;
dc->props = scsi_cd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_cd_info = {
@@ -1785,6 +1884,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data)
dc->desc = "SCSI block device passthrough";
dc->reset = scsi_disk_reset;
dc->props = scsi_block_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_block_info = {
@@ -1814,6 +1914,7 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
dc->desc = "virtual SCSI disk or CD-ROM (legacy)";
dc->reset = scsi_disk_reset;
dc->props = scsi_disk_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
}
static TypeInfo scsi_disk_info = {
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 86014aa..d856d23 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -59,6 +59,28 @@ typedef struct SCSIGenericReq {
sg_io_hdr_t io_header;
} SCSIGenericReq;
+static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+ qemu_put_sbe32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ assert(!r->req.sg);
+ qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
+ }
+}
+
+static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+ qemu_get_sbe32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ assert(!r->req.sg);
+ qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
+ }
+}
+
static void scsi_free_request(SCSIRequest *req)
{
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
@@ -446,6 +468,8 @@ const SCSIReqOps scsi_generic_req_ops = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .load_request = scsi_generic_load_request,
+ .save_request = scsi_generic_save_request,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
@@ -474,6 +498,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
dc->desc = "pass through generic scsi device (/dev/sg*)";
dc->reset = scsi_generic_reset;
dc->props = scsi_generic_properties;
+ dc->vmsd = &vmstate_scsi_device;
}
static TypeInfo scsi_generic_info = {
diff --git a/hw/scsi.h b/hw/scsi.h
index dc72b6f..2eb66f7 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -46,8 +46,11 @@ struct SCSIRequest {
uint32_t tag;
uint32_t lun;
uint32_t status;
+ size_t resid;
SCSICommand cmd;
BlockDriverAIOCB *aiocb;
+ QEMUSGList *sg;
+ bool dma_started;
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
bool enqueued;
@@ -93,6 +96,16 @@ struct SCSIDevice
uint64_t max_lba;
};
+extern const VMStateDescription vmstate_scsi_device;
+
+#define VMSTATE_SCSI_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SCSIDevice), \
+ .vmsd = &vmstate_scsi_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SCSIDevice), \
+}
+
/* cdrom.c */
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
@@ -106,14 +119,21 @@ struct SCSIReqOps {
void (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void (*load_request)(QEMUFile *f, SCSIRequest *req);
};
struct SCSIBusInfo {
int tcq;
int max_channel, max_target, max_lun;
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
- void (*complete)(SCSIRequest *req, uint32_t arg);
+ void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid);
void (*cancel)(SCSIRequest *req);
+ QEMUSGList *(*get_sg_list)(SCSIRequest *req);
+
+ void (*save_request)(QEMUFile *f, SCSIRequest *req);
+ void *(*load_request)(QEMUFile *f, SCSIRequest *req);
};
struct SCSIBus {
diff --git a/hw/serial.c b/hw/serial.c
index 144d1b3..c0ee55d 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -139,6 +139,7 @@ struct SerialState {
int it_shift;
int baudbase;
int tsr_retry;
+ uint32_t wakeup;
uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */
SerialFIFO recv_fifo;
@@ -635,6 +636,10 @@ static int serial_can_receive1(void *opaque)
static void serial_receive1(void *opaque, const uint8_t *buf, int size)
{
SerialState *s = opaque;
+
+ if (s->wakeup) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+ }
if(s->fcr & UART_FCR_FE) {
int i;
for (i = 0; i < size; i++) {
@@ -884,6 +889,7 @@ static Property serial_isa_properties[] = {
DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1),
DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1),
DEFINE_PROP_CHR("chardev", ISASerialState, state.chr),
+ DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index ffce261..2167017 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -494,7 +494,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
+static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, size_t resid)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = sreq->hba_private;
diff --git a/hw/tcx.c b/hw/tcx.c
index 2b66d86..ac7dcb4 100644
--- a/hw/tcx.c
+++ b/hw/tcx.c
@@ -56,8 +56,8 @@ typedef struct TCXState {
uint8_t dac_index, dac_state;
} TCXState;
-static void tcx_screen_dump(void *opaque, const char *filename);
-static void tcx24_screen_dump(void *opaque, const char *filename);
+static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch);
+static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch);
static void tcx_set_dirty(TCXState *s)
{
@@ -574,7 +574,7 @@ static int tcx_init1(SysBusDevice *dev)
return 0;
}
-static void tcx_screen_dump(void *opaque, const char *filename)
+static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch)
{
TCXState *s = opaque;
FILE *f;
@@ -601,7 +601,7 @@ static void tcx_screen_dump(void *opaque, const char *filename)
return;
}
-static void tcx24_screen_dump(void *opaque, const char *filename)
+static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch)
{
TCXState *s = opaque;
FILE *f;
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index c933efe..5fbd2d0 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -223,7 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
}
}
-static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
+static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
diff --git a/hw/vga.c b/hw/vga.c
index c1029db..5994f43 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -162,9 +162,7 @@ static uint32_t expand4[256];
static uint16_t expand2[256];
static uint8_t expand4to8[16];
-static void vga_screen_dump(void *opaque, const char *filename);
-static const char *screen_dump_filename;
-static DisplayChangeListener *screen_dump_dcl;
+static void vga_screen_dump(void *opaque, const char *filename, bool cswitch);
static void vga_update_memory_access(VGACommonState *s)
{
@@ -2364,22 +2362,6 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory)
/********************************************************/
/* vga screen dump */
-static void vga_save_dpy_update(DisplayState *ds,
- int x, int y, int w, int h)
-{
- if (screen_dump_filename) {
- ppm_save(screen_dump_filename, ds->surface);
- }
-}
-
-static void vga_save_dpy_resize(DisplayState *s)
-{
-}
-
-static void vga_save_dpy_refresh(DisplayState *s)
-{
-}
-
int ppm_save(const char *filename, struct DisplaySurface *ds)
{
FILE *f;
@@ -2423,29 +2405,15 @@ int ppm_save(const char *filename, struct DisplaySurface *ds)
return 0;
}
-static DisplayChangeListener* vga_screen_dump_init(DisplayState *ds)
-{
- DisplayChangeListener *dcl;
-
- dcl = g_malloc0(sizeof(DisplayChangeListener));
- dcl->dpy_update = vga_save_dpy_update;
- dcl->dpy_resize = vga_save_dpy_resize;
- dcl->dpy_refresh = vga_save_dpy_refresh;
- register_displaychangelistener(ds, dcl);
- return dcl;
-}
-
/* save the vga display in a PPM image even if no display is
available */
-static void vga_screen_dump(void *opaque, const char *filename)
+static void vga_screen_dump(void *opaque, const char *filename, bool cswitch)
{
VGACommonState *s = opaque;
- if (!screen_dump_dcl)
- screen_dump_dcl = vga_screen_dump_init(s->ds);
-
- screen_dump_filename = filename;
- vga_invalidate_display(s);
- vga_hw_update();
- screen_dump_filename = NULL;
+ if (cswitch) {
+ vga_invalidate_display(s);
+ vga_hw_update();
+ }
+ ppm_save(filename, s->ds->surface);
}
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index a5a4396..49990f8 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -69,7 +69,7 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
VirtIOBlock *s = req->dev;
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
@@ -77,14 +77,14 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
|| action == BLOCK_ERR_STOP_ANY) {
req->next = s->rq;
s->rq = req;
- bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->bs, error);
} else {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
bdrv_acct_done(s->bs, &req->acct);
g_free(req);
- bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 907b52a..a0fb7c1 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -21,6 +21,7 @@
#include "virtio-blk.h"
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
#include "pci.h"
#include "qemu-error.h"
#include "msix.h"
@@ -930,12 +931,67 @@ static TypeInfo virtio_balloon_info = {
.class_init = virtio_balloon_class_init,
};
+static int virtio_scsi_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi);
+ if (!vdev) {
+ return -EINVAL;
+ }
+
+ vdev->nvectors = proxy->nvectors;
+ virtio_init_pci(proxy, vdev);
+
+ /* make the actual value visible */
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+
+static int virtio_scsi_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_scsi_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
+static Property virtio_scsi_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_scsi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = virtio_scsi_init_pci;
+ k->exit = virtio_scsi_exit_pci;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+ k->revision = 0x00;
+ k->class_id = PCI_CLASS_STORAGE_SCSI;
+ dc->reset = virtio_pci_reset;
+ dc->props = virtio_scsi_properties;
+}
+
+static TypeInfo virtio_scsi_info = {
+ .name = "virtio-scsi-pci",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(VirtIOPCIProxy),
+ .class_init = virtio_scsi_class_init,
+};
+
static void virtio_pci_register_types(void)
{
type_register_static(&virtio_blk_info);
type_register_static(&virtio_net_info);
type_register_static(&virtio_serial_info);
type_register_static(&virtio_balloon_info);
+ type_register_static(&virtio_scsi_info);
}
type_init(virtio_pci_register_types)
diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
index 344c22b..e560428 100644
--- a/hw/virtio-pci.h
+++ b/hw/virtio-pci.h
@@ -17,6 +17,7 @@
#include "virtio-net.h"
#include "virtio-serial.h"
+#include "virtio-scsi.h"
/* Performance improves when virtqueue kick processing is decoupled from the
* vcpu thread using ioeventfd for some devices. */
@@ -40,6 +41,7 @@ typedef struct {
#endif
virtio_serial_conf serial;
virtio_net_conf net;
+ VirtIOSCSIConf scsi;
bool ioeventfd_disabled;
bool ioeventfd_started;
} VirtIOPCIProxy;
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
new file mode 100644
index 0000000..e607edc
--- /dev/null
+++ b/hw/virtio-scsi.c
@@ -0,0 +1,617 @@
+/*
+ * Virtio SCSI HBA
+ *
+ * Copyright IBM, Corp. 2010
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio-scsi.h"
+#include <hw/scsi.h>
+#include <hw/scsi-defs.h>
+
+#define VIRTIO_SCSI_VQ_SIZE 128
+#define VIRTIO_SCSI_CDB_SIZE 32
+#define VIRTIO_SCSI_SENSE_SIZE 96
+#define VIRTIO_SCSI_MAX_CHANNEL 0
+#define VIRTIO_SCSI_MAX_TARGET 255
+#define VIRTIO_SCSI_MAX_LUN 16383
+
+/* Response codes */
+#define VIRTIO_SCSI_S_OK 0
+#define VIRTIO_SCSI_S_OVERRUN 1
+#define VIRTIO_SCSI_S_ABORTED 2
+#define VIRTIO_SCSI_S_BAD_TARGET 3
+#define VIRTIO_SCSI_S_RESET 4
+#define VIRTIO_SCSI_S_BUSY 5
+#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
+#define VIRTIO_SCSI_S_TARGET_FAILURE 7
+#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
+#define VIRTIO_SCSI_S_FAILURE 9
+#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
+#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
+#define VIRTIO_SCSI_S_INCORRECT_LUN 12
+
+/* Controlq type codes. */
+#define VIRTIO_SCSI_T_TMF 0
+#define VIRTIO_SCSI_T_AN_QUERY 1
+#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2
+
+/* Valid TMF subtypes. */
+#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
+#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
+#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
+#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
+#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
+#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
+#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
+#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
+
+/* Events. */
+#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000
+#define VIRTIO_SCSI_T_NO_EVENT 0
+#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
+#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
+
+/* SCSI command request, followed by data-out */
+typedef struct {
+ uint8_t lun[8]; /* Logical Unit Number */
+ uint64_t tag; /* Command identifier */
+ uint8_t task_attr; /* Task attribute */
+ uint8_t prio;
+ uint8_t crn;
+ uint8_t cdb[];
+} QEMU_PACKED VirtIOSCSICmdReq;
+
+/* Response, followed by sense data and data-in */
+typedef struct {
+ uint32_t sense_len; /* Sense data length */
+ uint32_t resid; /* Residual bytes in data buffer */
+ uint16_t status_qualifier; /* Status qualifier */
+ uint8_t status; /* Command completion status */
+ uint8_t response; /* Response values */
+ uint8_t sense[];
+} QEMU_PACKED VirtIOSCSICmdResp;
+
+/* Task Management Request */
+typedef struct {
+ uint32_t type;
+ uint32_t subtype;
+ uint8_t lun[8];
+ uint64_t tag;
+} QEMU_PACKED VirtIOSCSICtrlTMFReq;
+
+typedef struct {
+ uint8_t response;
+} QEMU_PACKED VirtIOSCSICtrlTMFResp;
+
+/* Asynchronous notification query/subscription */
+typedef struct {
+ uint32_t type;
+ uint8_t lun[8];
+ uint32_t event_requested;
+} QEMU_PACKED VirtIOSCSICtrlANReq;
+
+typedef struct {
+ uint32_t event_actual;
+ uint8_t response;
+} QEMU_PACKED VirtIOSCSICtrlANResp;
+
+typedef struct {
+ uint32_t event;
+ uint8_t lun[8];
+ uint32_t reason;
+} QEMU_PACKED VirtIOSCSIEvent;
+
+typedef struct {
+ uint32_t num_queues;
+ uint32_t seg_max;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+ uint32_t event_info_size;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ uint16_t max_channel;
+ uint16_t max_target;
+ uint32_t max_lun;
+} QEMU_PACKED VirtIOSCSIConfig;
+
+typedef struct {
+ VirtIODevice vdev;
+ DeviceState *qdev;
+ VirtIOSCSIConf *conf;
+
+ SCSIBus bus;
+ VirtQueue *ctrl_vq;
+ VirtQueue *event_vq;
+ VirtQueue *cmd_vq;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ int resetting;
+} VirtIOSCSI;
+
+typedef struct VirtIOSCSIReq {
+ VirtIOSCSI *dev;
+ VirtQueue *vq;
+ VirtQueueElement elem;
+ QEMUSGList qsgl;
+ SCSIRequest *sreq;
+ union {
+ char *buf;
+ VirtIOSCSICmdReq *cmd;
+ VirtIOSCSICtrlTMFReq *tmf;
+ VirtIOSCSICtrlANReq *an;
+ } req;
+ union {
+ char *buf;
+ VirtIOSCSICmdResp *cmd;
+ VirtIOSCSICtrlTMFResp *tmf;
+ VirtIOSCSICtrlANResp *an;
+ VirtIOSCSIEvent *event;
+ } resp;
+} VirtIOSCSIReq;
+
+static inline int virtio_scsi_get_lun(uint8_t *lun)
+{
+ return ((lun[2] << 8) | lun[3]) & 0x3FFF;
+}
+
+static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
+{
+ if (lun[0] != 1) {
+ return NULL;
+ }
+ if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) {
+ return NULL;
+ }
+ return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
+}
+
+static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
+{
+ VirtIOSCSI *s = req->dev;
+ VirtQueue *vq = req->vq;
+ virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len);
+ qemu_sglist_destroy(&req->qsgl);
+ if (req->sreq) {
+ req->sreq->hba_private = NULL;
+ scsi_req_unref(req->sreq);
+ }
+ g_free(req);
+ virtio_notify(&s->vdev, vq);
+}
+
+static void virtio_scsi_bad_req(void)
+{
+ error_report("wrong size for virtio-scsi headers");
+ exit(1);
+}
+
+static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
+ target_phys_addr_t *addr, int num)
+{
+ memset(qsgl, 0, sizeof(*qsgl));
+ while (num--) {
+ qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
+ }
+}
+
+static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq,
+ VirtIOSCSIReq *req)
+{
+ assert(req->elem.out_num && req->elem.in_num);
+ req->vq = vq;
+ req->dev = s;
+ req->sreq = NULL;
+ req->req.buf = req->elem.out_sg[0].iov_base;
+ req->resp.buf = req->elem.in_sg[0].iov_base;
+
+ if (req->elem.out_num > 1) {
+ qemu_sgl_init_external(&req->qsgl, &req->elem.out_sg[1],
+ &req->elem.out_addr[1],
+ req->elem.out_num - 1);
+ } else {
+ qemu_sgl_init_external(&req->qsgl, &req->elem.in_sg[1],
+ &req->elem.in_addr[1],
+ req->elem.in_num - 1);
+ }
+}
+
+static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
+{
+ VirtIOSCSIReq *req;
+ req = g_malloc(sizeof(*req));
+ if (!virtqueue_pop(vq, &req->elem)) {
+ g_free(req);
+ return NULL;
+ }
+
+ virtio_scsi_parse_req(s, vq, req);
+ return req;
+}
+
+static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ VirtIOSCSIReq *req = sreq->hba_private;
+
+ qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+}
+
+static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ SCSIBus *bus = sreq->bus;
+ VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
+ VirtIOSCSIReq *req;
+
+ req = g_malloc(sizeof(*req));
+ qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+ virtio_scsi_parse_req(s, s->cmd_vq, req);
+
+ scsi_req_ref(sreq);
+ req->sreq = sreq;
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ int req_mode =
+ (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
+
+ assert(req->sreq->cmd.mode == req_mode);
+ }
+ return req;
+}
+
+static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
+{
+ SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun);
+ SCSIRequest *r, *next;
+ DeviceState *qdev;
+ int target;
+
+ /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
+ req->resp.tmf->response = VIRTIO_SCSI_S_OK;
+
+ switch (req->req.tmf->subtype) {
+ case VIRTIO_SCSI_T_TMF_ABORT_TASK:
+ case VIRTIO_SCSI_T_TMF_QUERY_TASK:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
+ if (r->tag == req->req.tmf->tag) {
+ break;
+ }
+ }
+ if (r && r->hba_private) {
+ if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
+ /* "If the specified command is present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
+ } else {
+ scsi_req_cancel(r);
+ }
+ }
+ break;
+
+ case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ s->resetting++;
+ qdev_reset_all(&d->qdev);
+ s->resetting--;
+ break;
+
+ case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
+ case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
+ case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
+ if (!d) {
+ goto fail;
+ }
+ if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
+ goto incorrect_lun;
+ }
+ QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
+ if (r->hba_private) {
+ if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
+ /* "If there is any command present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
+ break;
+ } else {
+ scsi_req_cancel(r);
+ }
+ }
+ }
+ break;
+
+ case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
+ target = req->req.tmf->lun[1];
+ s->resetting++;
+ QTAILQ_FOREACH(qdev, &s->bus.qbus.children, sibling) {
+ d = DO_UPCAST(SCSIDevice, qdev, qdev);
+ if (d->channel == 0 && d->id == target) {
+ qdev_reset_all(&d->qdev);
+ }
+ }
+ s->resetting--;
+ break;
+
+ case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
+ default:
+ req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
+ break;
+ }
+
+ return;
+
+incorrect_lun:
+ req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
+ return;
+
+fail:
+ req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
+}
+
+static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+ VirtIOSCSIReq *req;
+
+ while ((req = virtio_scsi_pop_req(s, vq))) {
+ int out_size, in_size;
+ if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+ virtio_scsi_bad_req();
+ continue;
+ }
+
+ out_size = req->elem.out_sg[0].iov_len;
+ in_size = req->elem.in_sg[0].iov_len;
+ if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) {
+ if (out_size < sizeof(VirtIOSCSICtrlTMFReq) ||
+ in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
+ virtio_scsi_bad_req();
+ }
+ virtio_scsi_do_tmf(s, req);
+
+ } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY ||
+ req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
+ if (out_size < sizeof(VirtIOSCSICtrlANReq) ||
+ in_size < sizeof(VirtIOSCSICtrlANResp)) {
+ virtio_scsi_bad_req();
+ }
+ req->resp.an->event_actual = 0;
+ req->resp.an->response = VIRTIO_SCSI_S_OK;
+ }
+ virtio_scsi_complete_req(req);
+ }
+}
+
+static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
+ size_t resid)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ req->resp.cmd->response = VIRTIO_SCSI_S_OK;
+ req->resp.cmd->status = status;
+ if (req->resp.cmd->status == GOOD) {
+ req->resp.cmd->resid = resid;
+ } else {
+ req->resp.cmd->resid = 0;
+ req->resp.cmd->sense_len =
+ scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE);
+ }
+ virtio_scsi_complete_req(req);
+}
+
+static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ return &req->qsgl;
+}
+
+static void virtio_scsi_request_cancelled(SCSIRequest *r)
+{
+ VirtIOSCSIReq *req = r->hba_private;
+
+ if (!req) {
+ return;
+ }
+ if (req->dev->resetting) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_RESET;
+ } else {
+ req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED;
+ }
+ virtio_scsi_complete_req(req);
+}
+
+static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
+{
+ req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE;
+ virtio_scsi_complete_req(req);
+}
+
+static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+ VirtIOSCSIReq *req;
+ int n;
+
+ while ((req = virtio_scsi_pop_req(s, vq))) {
+ SCSIDevice *d;
+ int out_size, in_size;
+ if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+ virtio_scsi_bad_req();
+ }
+
+ out_size = req->elem.out_sg[0].iov_len;
+ in_size = req->elem.in_sg[0].iov_len;
+ if (out_size < sizeof(VirtIOSCSICmdReq) + s->cdb_size ||
+ in_size < sizeof(VirtIOSCSICmdResp) + s->sense_size) {
+ virtio_scsi_bad_req();
+ }
+
+ if (req->elem.out_num > 1 && req->elem.in_num > 1) {
+ virtio_scsi_fail_cmd_req(req);
+ continue;
+ }
+
+ d = virtio_scsi_device_find(s, req->req.cmd->lun);
+ if (!d) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET;
+ virtio_scsi_complete_req(req);
+ continue;
+ }
+ req->sreq = scsi_req_new(d, req->req.cmd->tag,
+ virtio_scsi_get_lun(req->req.cmd->lun),
+ req->req.cmd->cdb, req);
+
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ int req_mode =
+ (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
+
+ if (req->sreq->cmd.mode != req_mode ||
+ req->sreq->cmd.xfer > req->qsgl.size) {
+ req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN;
+ virtio_scsi_complete_req(req);
+ continue;
+ }
+ }
+
+ n = scsi_req_enqueue(req->sreq);
+ if (n) {
+ scsi_req_continue(req->sreq);
+ }
+ }
+}
+
+static void virtio_scsi_get_config(VirtIODevice *vdev,
+ uint8_t *config)
+{
+ VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ stl_raw(&scsiconf->num_queues, s->conf->num_queues);
+ stl_raw(&scsiconf->seg_max, 128 - 2);
+ stl_raw(&scsiconf->max_sectors, s->conf->max_sectors);
+ stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun);
+ stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent));
+ stl_raw(&scsiconf->sense_size, s->sense_size);
+ stl_raw(&scsiconf->cdb_size, s->cdb_size);
+ stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL);
+ stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET);
+ stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN);
+}
+
+static void virtio_scsi_set_config(VirtIODevice *vdev,
+ const uint8_t *config)
+{
+ VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 ||
+ (uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) {
+ error_report("bad data written to virtio-scsi configuration space");
+ exit(1);
+ }
+
+ s->sense_size = ldl_raw(&scsiconf->sense_size);
+ s->cdb_size = ldl_raw(&scsiconf->cdb_size);
+}
+
+static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
+ uint32_t requested_features)
+{
+ return requested_features;
+}
+
+static void virtio_scsi_reset(VirtIODevice *vdev)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
+ s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
+}
+
+/* The device does not have anything to save beyond the virtio data.
+ * Request data is saved with callbacks from SCSI devices.
+ */
+static void virtio_scsi_save(QEMUFile *f, void *opaque)
+{
+ VirtIOSCSI *s = opaque;
+ virtio_save(&s->vdev, f);
+}
+
+static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOSCSI *s = opaque;
+ virtio_load(&s->vdev, f);
+ return 0;
+}
+
+static struct SCSIBusInfo virtio_scsi_scsi_info = {
+ .tcq = true,
+ .max_channel = VIRTIO_SCSI_MAX_CHANNEL,
+ .max_target = VIRTIO_SCSI_MAX_TARGET,
+ .max_lun = VIRTIO_SCSI_MAX_LUN,
+
+ .complete = virtio_scsi_command_complete,
+ .cancel = virtio_scsi_request_cancelled,
+ .get_sg_list = virtio_scsi_get_sg_list,
+ .save_request = virtio_scsi_save_request,
+ .load_request = virtio_scsi_load_request,
+};
+
+VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
+{
+ VirtIOSCSI *s;
+ static int virtio_scsi_id;
+
+ s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
+ sizeof(VirtIOSCSIConfig),
+ sizeof(VirtIOSCSI));
+
+ s->qdev = dev;
+ s->conf = proxyconf;
+
+ /* TODO set up vdev function pointers */
+ s->vdev.get_config = virtio_scsi_get_config;
+ s->vdev.set_config = virtio_scsi_set_config;
+ s->vdev.get_features = virtio_scsi_get_features;
+ s->vdev.reset = virtio_scsi_reset;
+
+ s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ virtio_scsi_handle_ctrl);
+ s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ NULL);
+ s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ virtio_scsi_handle_cmd);
+
+ scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
+ if (!dev->hotplugged) {
+ scsi_bus_legacy_handle_cmdline(&s->bus);
+ }
+
+ register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
+ virtio_scsi_save, virtio_scsi_load, s);
+
+ return &s->vdev;
+}
+
+void virtio_scsi_exit(VirtIODevice *vdev)
+{
+ virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h
new file mode 100644
index 0000000..4bc889d
--- /dev/null
+++ b/hw/virtio-scsi.h
@@ -0,0 +1,36 @@
+/*
+ * Virtio SCSI HBA
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_SCSI_H
+#define _QEMU_VIRTIO_SCSI_H
+
+#include "virtio.h"
+#include "net.h"
+#include "pci.h"
+
+/* The ID for virtio_scsi */
+#define VIRTIO_ID_SCSI 8
+
+struct VirtIOSCSIConf {
+ uint32_t num_queues;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+};
+
+#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _features_field, _conf_field) \
+ DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \
+ DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \
+ DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \
+ DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128)
+
+#endif /* _QEMU_VIRTIO_SCSI_H */
diff --git a/hw/virtio.h b/hw/virtio.h
index 25f5564..400c092 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -199,6 +199,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
typedef struct virtio_serial_conf virtio_serial_conf;
VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
VirtIODevice *virtio_balloon_init(DeviceState *dev);
+typedef struct VirtIOSCSIConf VirtIOSCSIConf;
+VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
#ifdef CONFIG_LINUX
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
#endif
@@ -208,6 +210,7 @@ void virtio_net_exit(VirtIODevice *vdev);
void virtio_blk_exit(VirtIODevice *vdev);
void virtio_serial_exit(VirtIODevice *vdev);
void virtio_balloon_exit(VirtIODevice *vdev);
+void virtio_scsi_exit(VirtIODevice *vdev);
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index f8afa3c..142d9f4 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1003,11 +1003,11 @@ static void vmsvga_invalidate_display(void *opaque)
/* save the vga display in a PPM image even if no display is
available */
-static void vmsvga_screen_dump(void *opaque, const char *filename)
+static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch)
{
struct vmsvga_state_s *s = opaque;
if (!s->enable) {
- s->vga.screen_dump(&s->vga, filename);
+ s->vga.screen_dump(&s->vga, filename, cswitch);
return;
}
diff --git a/hw/vt82c686.c b/hw/vt82c686.c
index fbab0bb..6fb7950 100644
--- a/hw/vt82c686.c
+++ b/hw/vt82c686.c
@@ -159,10 +159,8 @@ static void vt82c686b_write_config(PCIDevice * d, uint32_t address,
typedef struct VT686PMState {
PCIDevice dev;
- ACPIPM1EVT pm1a;
- ACPIPM1CNT pm1_cnt;
+ ACPIREGS ar;
APMState apm;
- ACPIPMTimer tmr;
PMSMBus smb;
uint32_t smb_io_base;
} VT686PMState;
@@ -179,21 +177,21 @@ static void pm_update_sci(VT686PMState *s)
{
int sci_level, pmsts;
- pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
- sci_level = (((pmsts & s->pm1a.en) &
+ pmsts = acpi_pm1_evt_get_sts(&s->ar);
+ sci_level = (((pmsts & s->ar.pm1.evt.en) &
(ACPI_BITMASK_RT_CLOCK_ENABLE |
ACPI_BITMASK_POWER_BUTTON_ENABLE |
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
ACPI_BITMASK_TIMER_ENABLE)) != 0);
qemu_set_irq(s->dev.irq[0], sci_level);
/* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
+ acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
!(pmsts & ACPI_BITMASK_TIMER_STATUS));
}
-static void pm_tmr_timer(ACPIPMTimer *tmr)
+static void pm_tmr_timer(ACPIREGS *ar)
{
- VT686PMState *s = container_of(tmr, VT686PMState, tmr);
+ VT686PMState *s = container_of(ar, VT686PMState, ar);
pm_update_sci(s);
}
@@ -204,15 +202,15 @@ static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
addr &= 0x0f;
switch (addr) {
case 0x00:
- acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val);
+ acpi_pm1_evt_write_sts(&s->ar, val);
pm_update_sci(s);
break;
case 0x02:
- s->pm1a.en = val;
+ acpi_pm1_evt_write_en(&s->ar, val);
pm_update_sci(s);
break;
case 0x04:
- acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val);
+ acpi_pm1_cnt_write(&s->ar, val);
break;
default:
break;
@@ -228,13 +226,13 @@ static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
addr &= 0x0f;
switch (addr) {
case 0x00:
- val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time);
+ val = acpi_pm1_evt_get_sts(&s->ar);
break;
case 0x02:
- val = s->pm1a.en;
+ val = s->ar.pm1.evt.en;
break;
case 0x04:
- val = s->pm1_cnt.cnt;
+ val = s->ar.pm1.cnt.cnt;
break;
default:
val = 0;
@@ -258,7 +256,7 @@ static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
addr &= 0x0f;
switch (addr) {
case 0x08:
- val = acpi_pm_tmr_get(&s->tmr);
+ val = acpi_pm_tmr_get(&s->ar);
break;
default:
val = 0;
@@ -309,12 +307,12 @@ static const VMStateDescription vmstate_acpi = {
.post_load = vmstate_acpi_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, VT686PMState),
- VMSTATE_UINT16(pm1a.sts, VT686PMState),
- VMSTATE_UINT16(pm1a.en, VT686PMState),
- VMSTATE_UINT16(pm1_cnt.cnt, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.evt.sts, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.evt.en, VT686PMState),
+ VMSTATE_UINT16(ar.pm1.cnt.cnt, VT686PMState),
VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState),
- VMSTATE_TIMER(tmr.timer, VT686PMState),
- VMSTATE_INT64(tmr.overflow_time, VT686PMState),
+ VMSTATE_TIMER(ar.tmr.timer, VT686PMState),
+ VMSTATE_INT64(ar.tmr.overflow_time, VT686PMState),
VMSTATE_END_OF_LIST()
}
};
@@ -431,8 +429,8 @@ static int vt82c686b_pm_initfn(PCIDevice *dev)
apm_init(&s->apm, NULL, s);
- acpi_pm_tmr_init(&s->tmr, pm_tmr_timer);
- acpi_pm1_cnt_init(&s->pm1_cnt, NULL);
+ acpi_pm_tmr_init(&s->ar, pm_tmr_timer);
+ acpi_pm1_cnt_init(&s->ar);
pm_smbus_init(&s->dev.qdev, &s->smb);