diff options
Diffstat (limited to 'hw')
46 files changed, 1368 insertions, 412 deletions
@@ -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; @@ -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); } @@ -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; } @@ -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); } @@ -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, @@ -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; @@ -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); } @@ -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; @@ -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: @@ -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 = { @@ -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; @@ -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; @@ -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); |