diff options
Diffstat (limited to 'hw')
292 files changed, 12352 insertions, 2384 deletions
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 791a5572bb..b8220abae7 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -184,10 +184,10 @@ static TypeInfo virtio_9p_info = { .class_init = virtio_9p_class_init, }; -static void virtio_9p_register_devices(void) +static void virtio_9p_register_types(void) { type_register_static(&virtio_9p_info); virtio_9p_set_fd_limit(); } -device_init(virtio_9p_register_devices) +type_init(virtio_9p_register_types) diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index a72ffc3390..c633fb9b7e 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -1349,7 +1349,9 @@ static void v9fs_open(void *opaque) if (s->proto_version == V9FS_PROTO_2000L) { err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); } else { - err = pdu_unmarshal(pdu, offset, "db", &fid, &mode); + uint8_t modebyte; + err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte); + mode = modebyte; } if (err < 0) { goto out_nofid; @@ -3260,9 +3262,9 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) ptr = pdu->elem.out_sg[0].iov_base; - memcpy(&pdu->size, ptr, 4); + pdu->size = le32_to_cpu(*(uint32_t *)ptr); pdu->id = ptr[4]; - memcpy(&pdu->tag, ptr + 5, 2); + pdu->tag = le16_to_cpu(*(uint16_t *)(ptr + 5)); qemu_co_queue_init(&pdu->complete); submit_pdu(s, pdu); } diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c new file mode 100644 index 0000000000..71142e51f5 --- /dev/null +++ b/hw/a15mpcore.c @@ -0,0 +1,103 @@ +/* + * Cortex-A15MPCore internal peripheral emulation. + * + * Copyright (c) 2012 Linaro Limited. + * Written by Peter Maydell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" + +/* Configuration for arm_gic.c: + * max number of CPUs, how to ID current CPU + */ +#define NCPU 4 + +static inline int gic_get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +#include "arm_gic.c" + +/* A15MP private memory region. */ + +typedef struct A15MPPrivState { + gic_state gic; + uint32_t num_cpu; + uint32_t num_irq; + MemoryRegion container; +} A15MPPrivState; + +static int a15mp_priv_init(SysBusDevice *dev) +{ + A15MPPrivState *s = FROM_SYSBUSGIC(A15MPPrivState, dev); + + if (s->num_cpu > NCPU) { + hw_error("a15mp_priv_init: num-cpu may not be more than %d\n", NCPU); + } + + gic_init(&s->gic, s->num_cpu, s->num_irq); + + /* Memory map (addresses are offsets from PERIPHBASE): + * 0x0000-0x0fff -- reserved + * 0x1000-0x1fff -- GIC Distributor + * 0x2000-0x2fff -- GIC CPU interface + * 0x4000-0x4fff -- GIC virtual interface control (not modelled) + * 0x5000-0x5fff -- GIC virtual interface control (not modelled) + * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) + */ + memory_region_init(&s->container, "a15mp-priv-container", 0x8000); + memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); + memory_region_add_subregion(&s->container, 0x2000, &s->gic.cpuiomem[0]); + + sysbus_init_mmio(dev, &s->container); + return 0; +} + +static Property a15mp_priv_properties[] = { + DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1), + /* The Cortex-A15MP may have anything from 0 to 224 external interrupt + * IRQ lines (with another 32 internal). We default to 64+32, which + * is the number provided by the Cortex-A15MP test chip in the + * Versatile Express A15 development board. + * Other boards may differ and should set this property appropriately. + */ + DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 96), + DEFINE_PROP_END_OF_LIST(), +}; + +static void a15mp_priv_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = a15mp_priv_init; + dc->props = a15mp_priv_properties; + /* We currently have no savable state outside the common GIC state */ +} + +static TypeInfo a15mp_priv_info = { + .name = "a15mpcore_priv", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A15MPPrivState), + .class_init = a15mp_priv_class_init, +}; + +static void a15mp_register_types(void) +{ + type_register_static(&a15mp_priv_info); +} + +type_init(a15mp_register_types) diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 19de12b4b4..03b128ce5b 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -238,9 +238,9 @@ static TypeInfo a9mp_priv_info = { .class_init = a9mp_priv_class_init, }; -static void a9mp_register_devices(void) +static void a9mp_register_types(void) { type_register_static(&a9mp_priv_info); } -device_init(a9mp_register_devices) +type_init(a9mp_register_types) @@ -1372,9 +1372,9 @@ static TypeInfo ac97_info = { .class_init = ac97_class_init, }; -static void ac97_register (void) +static void ac97_register_types (void) { type_register_static (&ac97_info); } -device_init (ac97_register); +type_init (ac97_register_types) @@ -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 21484aec42..797ed245fc 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; @@ -426,17 +422,17 @@ static TypeInfo piix4_pm_info = { .class_init = piix4_pm_class_init, }; -static void piix4_pm_register(void) +static void piix4_pm_register_types(void) { type_register_static(&piix4_pm_info); } -device_init(piix4_pm_register); +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/ads7846.c b/hw/ads7846.c index 3cfdecbe5c..41c7f101c4 100644 --- a/hw/ads7846.c +++ b/hw/ads7846.c @@ -168,9 +168,9 @@ static TypeInfo ads7846_info = { .class_init = ads7846_class_init, }; -static void ads7846_register_devices(void) +static void ads7846_register_types(void) { type_register_static(&ads7846_info); } -device_init(ads7846_register_devices) +type_init(ads7846_register_types) diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c index 876335a773..ea0fd95f84 100644 --- a/hw/alpha_dp264.c +++ b/hw/alpha_dp264.c @@ -14,6 +14,7 @@ #include "sysemu.h" #include "mc146818rtc.h" #include "ide.h" +#include "i8254.h" #define MAX_IDE_BUS 2 @@ -72,7 +73,7 @@ static void clipper_init(ram_addr_t ram_size, clipper_pci_map_irq); rtc_init(isa_bus, 1980, rtc_irq); - pit_init(isa_bus, 0x40, 0); + pit_init(isa_bus, 0x40, 0, NULL); isa_create_simple(isa_bus, "i8042"); /* VGA setup. Don't bother loading the bios. */ diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index 736c28a3a9..b539416589 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -824,8 +824,9 @@ static TypeInfo typhoon_pcihost_info = { .class_init = typhoon_pcihost_class_init, }; -static void typhoon_register(void) +static void typhoon_register_types(void) { type_register_static(&typhoon_pcihost_info); } -device_init(typhoon_register); + +type_init(typhoon_register_types) diff --git a/hw/apb_pci.c b/hw/apb_pci.c index c7aaa72d07..1d25da8da9 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -493,11 +493,11 @@ static TypeInfo pbm_pci_bridge_info = { .class_init = pbm_pci_bridge_class_init, }; -static void pbm_register_devices(void) +static void pbm_register_types(void) { type_register_static(&pbm_host_info); type_register_static(&pbm_pci_host_info); type_register_static(&pbm_pci_bridge_info); } -device_init(pbm_register_devices) +type_init(pbm_register_types) @@ -35,6 +35,10 @@ #define MSI_ADDR_DEST_ID_SHIFT 12 #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 +#define SYNC_FROM_VAPIC 0x1 +#define SYNC_TO_VAPIC 0x2 +#define SYNC_ISR_IRR_TO_VAPIC 0x4 + static APICCommonState *local_apics[MAX_APICS + 1]; static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode); @@ -78,6 +82,70 @@ static inline int get_bit(uint32_t *tab, int index) return !!(tab[i] & mask); } +/* return -1 if no bit is set */ +static int get_highest_priority_int(uint32_t *tab) +{ + int i; + for (i = 7; i >= 0; i--) { + if (tab[i] != 0) { + return i * 32 + fls_bit(tab[i]); + } + } + return -1; +} + +static void apic_sync_vapic(APICCommonState *s, int sync_type) +{ + VAPICState vapic_state; + size_t length; + off_t start; + int vector; + + if (!s->vapic_paddr) { + return; + } + if (sync_type & SYNC_FROM_VAPIC) { + cpu_physical_memory_rw(s->vapic_paddr, (void *)&vapic_state, + sizeof(vapic_state), 0); + s->tpr = vapic_state.tpr; + } + if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) { + start = offsetof(VAPICState, isr); + length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr); + + if (sync_type & SYNC_TO_VAPIC) { + assert(qemu_cpu_is_self(s->cpu_env)); + + vapic_state.tpr = s->tpr; + vapic_state.enabled = 1; + start = 0; + length = sizeof(VAPICState); + } + + vector = get_highest_priority_int(s->isr); + if (vector < 0) { + vector = 0; + } + vapic_state.isr = vector & 0xf0; + + vapic_state.zero = 0; + + vector = get_highest_priority_int(s->irr); + if (vector < 0) { + vector = 0; + } + vapic_state.irr = vector & 0xff; + + cpu_physical_memory_write_rom(s->vapic_paddr + start, + ((void *)&vapic_state) + start, length); + } +} + +static void apic_vapic_base_update(APICCommonState *s) +{ + apic_sync_vapic(s, SYNC_TO_VAPIC); +} + static void apic_local_deliver(APICCommonState *s, int vector) { uint32_t lvt = s->lvt[vector]; @@ -239,20 +307,17 @@ static void apic_set_base(APICCommonState *s, uint64_t val) static void apic_set_tpr(APICCommonState *s, uint8_t val) { - s->tpr = (val & 0x0f) << 4; - apic_update_irq(s); + /* Updates from cr8 are ignored while the VAPIC is active */ + if (!s->vapic_paddr) { + s->tpr = val << 4; + apic_update_irq(s); + } } -/* return -1 if no bit is set */ -static int get_highest_priority_int(uint32_t *tab) +static uint8_t apic_get_tpr(APICCommonState *s) { - int i; - for(i = 7; i >= 0; i--) { - if (tab[i] != 0) { - return i * 32 + fls_bit(tab[i]); - } - } - return -1; + apic_sync_vapic(s, SYNC_FROM_VAPIC); + return s->tpr >> 4; } static int apic_get_ppr(APICCommonState *s) @@ -312,6 +377,14 @@ static void apic_update_irq(APICCommonState *s) } } +void apic_poll_irq(DeviceState *d) +{ + APICCommonState *s = APIC_COMMON(d); + + apic_sync_vapic(s, SYNC_FROM_VAPIC); + apic_update_irq(s); +} + static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) { apic_report_irq_delivered(!get_bit(s->irr, vector_num)); @@ -321,6 +394,16 @@ static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) set_bit(s->tmr, vector_num); else reset_bit(s->tmr, vector_num); + if (s->vapic_paddr) { + apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC); + /* + * The vcpu thread needs to see the new IRR before we pull its current + * TPR value. That way, if we miss a lowering of the TRP, the guest + * has the chance to notice the new IRR and poll for IRQs on its own. + */ + smp_wmb(); + apic_sync_vapic(s, SYNC_FROM_VAPIC); + } apic_update_irq(s); } @@ -334,6 +417,7 @@ static void apic_eoi(APICCommonState *s) if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) { ioapic_eoi_broadcast(isrv); } + apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC); apic_update_irq(s); } @@ -471,15 +555,19 @@ int apic_get_interrupt(DeviceState *d) if (!(s->spurious_vec & APIC_SV_ENABLE)) return -1; + apic_sync_vapic(s, SYNC_FROM_VAPIC); intno = apic_irq_pending(s); if (intno == 0) { + apic_sync_vapic(s, SYNC_TO_VAPIC); return -1; } else if (intno < 0) { + apic_sync_vapic(s, SYNC_TO_VAPIC); return s->spurious_vec & 0xff; } reset_bit(s->irr, intno); set_bit(s->isr, intno); + apic_sync_vapic(s, SYNC_TO_VAPIC); apic_update_irq(s); return intno; } @@ -576,6 +664,10 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */ break; case 0x08: + apic_sync_vapic(s, SYNC_FROM_VAPIC); + if (apic_report_tpr_access) { + cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_READ); + } val = s->tpr; break; case 0x09: @@ -675,7 +767,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) case 0x03: break; case 0x08: + if (apic_report_tpr_access) { + cpu_report_tpr_access(s->cpu_env, TPR_ACCESS_WRITE); + } s->tpr = val; + apic_sync_vapic(s, SYNC_TO_VAPIC); apic_update_irq(s); break; case 0x09: @@ -737,6 +833,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +static void apic_pre_save(APICCommonState *s) +{ + apic_sync_vapic(s, SYNC_FROM_VAPIC); +} + static void apic_post_load(APICCommonState *s) { if (s->timer_expiry != -1) { @@ -770,7 +871,10 @@ static void apic_class_init(ObjectClass *klass, void *data) k->init = apic_init; k->set_base = apic_set_base; k->set_tpr = apic_set_tpr; + k->get_tpr = apic_get_tpr; + k->vapic_base_update = apic_vapic_base_update; k->external_nmi = apic_external_nmi; + k->pre_save = apic_pre_save; k->post_load = apic_post_load; } @@ -781,9 +885,9 @@ static TypeInfo apic_info = { .class_init = apic_class_init, }; -static void apic_register_devices(void) +static void apic_register_types(void) { type_register_static(&apic_info); } -device_init(apic_register_devices) +type_init(apic_register_types) @@ -18,6 +18,8 @@ void cpu_set_apic_tpr(DeviceState *s, uint8_t val); uint8_t cpu_get_apic_tpr(DeviceState *s); void apic_init_reset(DeviceState *s); void apic_sipi(DeviceState *s); +void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, + TPRAccess access); /* pc.c */ int cpu_is_bsp(CPUState *env); diff --git a/hw/apic_common.c b/hw/apic_common.c index 26991b4516..60b82596e7 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -20,8 +20,10 @@ #include "apic.h" #include "apic_internal.h" #include "trace.h" +#include "kvm.h" static int apic_irq_delivered; +bool apic_report_tpr_access; void cpu_set_apic_base(DeviceState *d, uint64_t val) { @@ -63,9 +65,45 @@ void cpu_set_apic_tpr(DeviceState *d, uint8_t val) uint8_t cpu_get_apic_tpr(DeviceState *d) { + APICCommonState *s; + APICCommonClass *info; + + if (!d) { + return 0; + } + + s = APIC_COMMON(d); + info = APIC_COMMON_GET_CLASS(s); + + return info->get_tpr(s); +} + +void apic_enable_tpr_access_reporting(DeviceState *d, bool enable) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + apic_report_tpr_access = enable; + if (info->enable_tpr_reporting) { + info->enable_tpr_reporting(s, enable); + } +} + +void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr) +{ + APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + s->vapic_paddr = paddr; + info->vapic_base_update(s); +} + +void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, + TPRAccess access) +{ APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); - return s ? s->tpr >> 4 : 0; + vapic_report_tpr_access(s->vapic, s->cpu_env, ip, access); } void apic_report_irq_delivered(int delivered) @@ -166,12 +204,16 @@ void apic_init_reset(DeviceState *d) static void apic_reset_common(DeviceState *d) { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); bool bsp; bsp = cpu_is_bsp(s->cpu_env); s->apicbase = 0xfee00000 | (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; + s->vapic_paddr = 0; + info->vapic_base_update(s); + apic_init_reset(d); if (bsp) { @@ -188,6 +230,7 @@ static void apic_reset_common(DeviceState *d) static int apic_load_old(QEMUFile *f, void *opaque, int version_id) { APICCommonState *s = opaque; + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); int i; if (version_id > 2) { @@ -220,7 +263,11 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id) s->next_time = qemu_get_be64(f); if (version_id >= 2) { - qemu_get_timer(f, s->timer); + s->timer_expiry = qemu_get_be64(f); + } + + if (info->post_load) { + info->post_load(s); } return 0; } @@ -229,6 +276,7 @@ static int apic_init_common(SysBusDevice *dev) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; + static DeviceState *vapic; static int apic_no; if (apic_no >= MAX_APICS) { @@ -239,10 +287,29 @@ static int apic_init_common(SysBusDevice *dev) info = APIC_COMMON_GET_CLASS(s); info->init(s); - sysbus_init_mmio(&s->busdev, &s->io_memory); + sysbus_init_mmio(dev, &s->io_memory); + + if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK) { + vapic = sysbus_create_simple("kvmvapic", -1, NULL); + } + s->vapic = vapic; + if (apic_report_tpr_access && info->enable_tpr_reporting) { + info->enable_tpr_reporting(s, true); + } + return 0; } +static void apic_dispatch_pre_save(void *opaque) +{ + APICCommonState *s = APIC_COMMON(opaque); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + if (info->pre_save) { + info->pre_save(s); + } +} + static int apic_dispatch_post_load(void *opaque, int version_id) { APICCommonState *s = APIC_COMMON(opaque); @@ -260,6 +327,7 @@ static const VMStateDescription vmstate_apic_common = { .minimum_version_id = 3, .minimum_version_id_old = 1, .load_state_old = apic_load_old, + .pre_save = apic_dispatch_pre_save, .post_load = apic_dispatch_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(apicbase, APICCommonState), @@ -289,6 +357,8 @@ static const VMStateDescription vmstate_apic_common = { static Property apic_properties_common[] = { DEFINE_PROP_UINT8("id", APICCommonState, id, -1), DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env), + DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, + true), DEFINE_PROP_END_OF_LIST(), }; @@ -313,9 +383,9 @@ static TypeInfo apic_common_type = { .abstract = true, }; -static void register_devices(void) +static void register_types(void) { type_register_static(&apic_common_type); } -device_init(register_devices); +type_init(register_types) diff --git a/hw/apic_internal.h b/hw/apic_internal.h index 0cab010717..60a6a8bdae 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -61,6 +61,9 @@ #define APIC_SV_DIRECTED_IO (1<<12) #define APIC_SV_ENABLE (1<<8) +#define VAPIC_ENABLE_BIT 0 +#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT) + #define MAX_APICS 255 #define MSI_SPACE_SIZE 0x100000 @@ -82,7 +85,11 @@ typedef struct APICCommonClass void (*init)(APICCommonState *s); void (*set_base)(APICCommonState *s, uint64_t val); void (*set_tpr)(APICCommonState *s, uint8_t val); + uint8_t (*get_tpr)(APICCommonState *s); + void (*enable_tpr_reporting)(APICCommonState *s, bool enable); + void (*vapic_base_update)(APICCommonState *s); void (*external_nmi)(APICCommonState *s); + void (*pre_save)(APICCommonState *s); void (*post_load)(APICCommonState *s); } APICCommonClass; @@ -114,9 +121,29 @@ struct APICCommonState { int64_t timer_expiry; int sipi_vector; int wait_for_sipi; + + uint32_t vapic_control; + DeviceState *vapic; + target_phys_addr_t vapic_paddr; /* note: persistence via kvmvapic */ }; +typedef struct VAPICState { + uint8_t tpr; + uint8_t isr; + uint8_t zero; + uint8_t irr; + uint8_t enabled; +} QEMU_PACKED VAPICState; + +extern bool apic_report_tpr_access; + void apic_report_irq_delivered(int delivered); bool apic_next_timer(APICCommonState *s, int64_t current_time); +void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); +void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr); +void apic_poll_irq(DeviceState *d); + +void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, + TPRAccess access); #endif /* !QEMU_APIC_INTERNAL_H */ diff --git a/hw/applesmc.c b/hw/applesmc.c index b06487f70d..8bedaad310 100644 --- a/hw/applesmc.c +++ b/hw/applesmc.c @@ -243,9 +243,9 @@ static TypeInfo applesmc_isa_info = { .class_init = qdev_applesmc_class_init, }; -static void applesmc_register_devices(void) +static void applesmc_register_types(void) { type_register_static(&applesmc_isa_info); } -device_init(applesmc_register_devices) +type_init(applesmc_register_types) diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 5e5204bbf5..306013aeeb 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -37,7 +37,7 @@ struct arm_boot_info { */ target_phys_addr_t smp_loader_start; target_phys_addr_t smp_bootreg_addr; - target_phys_addr_t smp_priv_base; + target_phys_addr_t gic_cpu_if_addr; int nb_cpus; int board_id; int (*atag_board)(const struct arm_boot_info *info, void *p); diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 3c0839c4b4..102348bb70 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -252,10 +252,10 @@ static TypeInfo mpcore_priv_info = { .class_init = mpcore_priv_class_init, }; -static void arm11mpcore_register_devices(void) +static void arm11mpcore_register_types(void) { type_register_static(&mpcore_rirq_info); type_register_static(&mpcore_priv_info); } -device_init(arm11mpcore_register_devices) +type_init(arm11mpcore_register_types) diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 5f163fda02..2ef25ca9dd 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -43,16 +43,16 @@ static uint32_t bootloader[] = { * location for the kernel secondary CPU entry point. */ static uint32_t smpboot[] = { - 0xe59f201c, /* ldr r2, privbase */ + 0xe59f201c, /* ldr r2, gic_cpu_if */ 0xe59f001c, /* ldr r0, startaddr */ 0xe3a01001, /* mov r1, #1 */ - 0xe5821100, /* str r1, [r2, #256] */ + 0xe5821000, /* str r1, [r2] */ 0xe320f003, /* wfi */ 0xe5901000, /* ldr r1, [r0] */ 0xe1110001, /* tst r1, r1 */ 0x0afffffb, /* beq <wfi> */ 0xe12fff11, /* bx r1 */ - 0, /* privbase: Private memory region base address. */ + 0, /* gic_cpu_if: base address of GIC CPU interface */ 0 /* bootreg: Boot register address is held here */ }; @@ -61,7 +61,7 @@ static void default_write_secondary(CPUState *env, { int n; smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr; - smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base; + smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr; for (n = 0; n < ARRAY_SIZE(smpboot); n++) { smpboot[n] = tswap32(smpboot[n]); } @@ -81,9 +81,10 @@ static void default_reset_secondary(CPUState *env, p += 4; \ } while (0) -static void set_kernel_args(const struct arm_boot_info *info, - int initrd_size, target_phys_addr_t base) +static void set_kernel_args(const struct arm_boot_info *info) { + int initrd_size = info->initrd_size; + target_phys_addr_t base = info->loader_start; target_phys_addr_t p; p = base + KERNEL_ARGS_ADDR; @@ -134,12 +135,12 @@ static void set_kernel_args(const struct arm_boot_info *info, WRITE_WORD(p, 0); } -static void set_kernel_args_old(const struct arm_boot_info *info, - int initrd_size, target_phys_addr_t base) +static void set_kernel_args_old(const struct arm_boot_info *info) { target_phys_addr_t p; const char *s; - + int initrd_size = info->initrd_size; + target_phys_addr_t base = info->loader_start; /* see linux/include/asm-arm/setup.h */ p = base + KERNEL_ARGS_ADDR; @@ -222,11 +223,9 @@ static void do_cpu_reset(void *opaque) if (env == first_cpu) { env->regs[15] = info->loader_start; if (old_param) { - set_kernel_args_old(info, info->initrd_size, - info->loader_start); + set_kernel_args_old(info); } else { - set_kernel_args(info, info->initrd_size, - info->loader_start); + set_kernel_args(info); } } else { info->secondary_cpu_reset_hook(env, info); diff --git a/hw/arm_l2x0.c b/hw/arm_l2x0.c index ba26abc6f4..09f290c85f 100644 --- a/hw/arm_l2x0.c +++ b/hw/arm_l2x0.c @@ -184,9 +184,9 @@ static TypeInfo l2x0_info = { .class_init = l2x0_class_init, }; -static void l2x0_register_device(void) +static void l2x0_register_types(void) { type_register_static(&l2x0_info); } -device_init(l2x0_register_device) +type_init(l2x0_register_types) diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c index 5a02365b6f..361e887dec 100644 --- a/hw/arm_mptimer.c +++ b/hw/arm_mptimer.c @@ -335,9 +335,9 @@ static TypeInfo arm_mptimer_info = { .class_init = arm_mptimer_class_init, }; -static void arm_mptimer_register_devices(void) +static void arm_mptimer_register_types(void) { type_register_static(&arm_mptimer_info); } -device_init(arm_mptimer_register_devices) +type_init(arm_mptimer_register_types) diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 9d257994c0..5f1237b8c2 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -378,7 +378,7 @@ static void arm_sysctl_gpio_set(void *opaque, int line, int level) } } -static int arm_sysctl_init1(SysBusDevice *dev) +static int arm_sysctl_init(SysBusDevice *dev) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev); @@ -389,18 +389,6 @@ static int arm_sysctl_init1(SysBusDevice *dev) return 0; } -/* Legacy helper function. */ -void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "realview_sysctl"); - qdev_prop_set_uint32(dev, "sys_id", sys_id); - qdev_init_nofail(dev); - qdev_prop_set_uint32(dev, "proc_id", proc_id); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); -} - static Property arm_sysctl_properties[] = { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0), @@ -412,7 +400,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = arm_sysctl_init1; + k->init = arm_sysctl_init; dc->reset = arm_sysctl_reset; dc->vmsd = &vmstate_arm_sysctl; dc->props = arm_sysctl_properties; @@ -425,9 +413,9 @@ static TypeInfo arm_sysctl_info = { .class_init = arm_sysctl_class_init, }; -static void arm_sysctl_register_devices(void) +static void arm_sysctl_register_types(void) { type_register_static(&arm_sysctl_info); } -device_init(arm_sysctl_register_devices) +type_init(arm_sysctl_register_types) diff --git a/hw/arm_timer.c b/hw/arm_timer.c index 1019d41b3e..e3ecce29f0 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -383,10 +383,10 @@ static TypeInfo sp804_info = { .class_init = sp804_class_init, }; -static void arm_timer_register_devices(void) +static void arm_timer_register_types(void) { type_register_static(&icp_pit_info); type_register_static(&sp804_info); } -device_init(arm_timer_register_devices) +type_init(arm_timer_register_types) diff --git a/hw/armv7m.c b/hw/armv7m.c index de3d7e0828..6b805798e6 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -266,9 +266,9 @@ static TypeInfo bitband_info = { .class_init = bitband_class_init, }; -static void armv7m_register_devices(void) +static void armv7m_register_types(void) { type_register_static(&bitband_info); } -device_init(armv7m_register_devices) +type_init(armv7m_register_types) diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 1ed0abc3e7..3210129c3f 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -417,9 +417,9 @@ static TypeInfo armv7m_nvic_info = { .class_init = armv7m_nvic_class_init, }; -static void armv7m_nvic_register_devices(void) +static void armv7m_nvic_register_types(void) { type_register_static(&armv7m_nvic_info); } -device_init(armv7m_nvic_register_devices) +type_init(armv7m_nvic_register_types) @@ -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/bitbang_i2c.c b/hw/bitbang_i2c.c index c9c11823af..44ed7f4d61 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -237,9 +237,9 @@ static TypeInfo gpio_i2c_info = { .class_init = gpio_i2c_class_init, }; -static void bitbang_i2c_register(void) +static void bitbang_i2c_register_types(void) { type_register_static(&gpio_i2c_info); } -device_init(bitbang_i2c_register) +type_init(bitbang_i2c_register_types) diff --git a/hw/blizzard.c b/hw/blizzard.c index b2c1b22844..c7d844d105 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/boards.h b/hw/boards.h index f6d3784cf1..667177d76d 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -32,6 +32,7 @@ typedef struct QEMUMachine { } QEMUMachine; int qemu_register_machine(QEMUMachine *m); +QEMUMachine *find_default_machine(void); extern QEMUMachine *current_machine; diff --git a/hw/bonito.c b/hw/bonito.c index 7350a4f9ec..77786f8883 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -804,9 +804,10 @@ static TypeInfo bonito_pcihost_info = { .class_init = bonito_pcihost_class_init, }; -static void bonito_register(void) +static void bonito_register_types(void) { type_register_static(&bonito_pcihost_info); type_register_static(&bonito_info); } -device_init(bonito_register); + +type_init(bonito_register_types) diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 9510ed4d94..f4a6da4283 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -594,9 +594,9 @@ static TypeInfo emulated_card_info = { .class_init = emulated_class_initfn, }; -static void ccid_card_emulated_register_devices(void) +static void ccid_card_emulated_register_types(void) { type_register_static(&emulated_card_info); } -device_init(ccid_card_emulated_register_devices) +type_init(ccid_card_emulated_register_types) diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c index a7006ca035..bd6c77777d 100644 --- a/hw/ccid-card-passthru.c +++ b/hw/ccid-card-passthru.c @@ -343,9 +343,9 @@ static TypeInfo passthru_card_info = { .class_init = passthru_class_initfn, }; -static void ccid_card_passthru_register_devices(void) +static void ccid_card_passthru_register_types(void) { type_register_static(&passthru_card_info); } -device_init(ccid_card_passthru_register_devices) +type_init(ccid_card_passthru_register_types) diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index a8e8ab7660..4edcb94774 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -2923,12 +2923,6 @@ static TypeInfo isa_cirrus_vga_info = { .class_init = isa_cirrus_vga_class_init, }; -static void isa_cirrus_vga_register(void) -{ - type_register_static(&isa_cirrus_vga_info); -} -device_init(isa_cirrus_vga_register) - /*************************************** * * PCI bus support @@ -2996,8 +2990,10 @@ static TypeInfo cirrus_vga_info = { .class_init = cirrus_vga_class_init, }; -static void cirrus_vga_register(void) +static void cirrus_vga_register_types(void) { + type_register_static(&isa_cirrus_vga_info); type_register_static(&cirrus_vga_info); } -device_init(cirrus_vga_register); + +type_init(cirrus_vga_register_types) diff --git a/hw/cs4231.c b/hw/cs4231.c index c0badbff5c..cfec1d9cd1 100644 --- a/hw/cs4231.c +++ b/hw/cs4231.c @@ -173,9 +173,9 @@ static TypeInfo cs4231_info = { .class_init = cs4231_class_init, }; -static void cs4231_register_devices(void) +static void cs4231_register_types(void) { type_register_static(&cs4231_info); } -device_init(cs4231_register_devices) +type_init(cs4231_register_types) diff --git a/hw/cs4231a.c b/hw/cs4231a.c index ad04ad667c..e07b9d6237 100644 --- a/hw/cs4231a.c +++ b/hw/cs4231a.c @@ -689,8 +689,9 @@ static TypeInfo cs4231a_info = { .class_init = cs4231a_class_initfn, }; -static void cs4231a_register (void) +static void cs4231a_register_types (void) { type_register_static (&cs4231a_info); } -device_init (cs4231a_register) + +type_init (cs4231a_register_types) diff --git a/hw/debugcon.c b/hw/debugcon.c index 3903b2605d..14ab326be3 100644 --- a/hw/debugcon.c +++ b/hw/debugcon.c @@ -109,9 +109,9 @@ static TypeInfo debugcon_isa_info = { .class_init = debugcon_isa_class_initfn, }; -static void debugcon_register_devices(void) +static void debugcon_register_types(void) { type_register_static(&debugcon_isa_info); } -device_init(debugcon_register_devices) +type_init(debugcon_register_types) diff --git a/hw/dec_pci.c b/hw/dec_pci.c index a40fbcf3e5..37337bf4b6 100644 --- a/hw/dec_pci.c +++ b/hw/dec_pci.c @@ -140,11 +140,11 @@ static TypeInfo pci_dec_21154_device_info = { .class_init = pci_dec_21154_device_class_init, }; -static void dec_register_devices(void) +static void dec_register_types(void) { type_register_static(&pci_dec_21154_device_info); type_register_static(&dec_21154_pci_host_info); type_register_static(&dec_21154_pci_bridge_info); } -device_init(dec_register_devices) +type_init(dec_register_types) diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 539bcebae0..2cd355bd0a 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -157,9 +157,9 @@ static TypeInfo nvram_sysbus_info = { .class_init = nvram_sysbus_class_init, }; -static void nvram_register(void) +static void nvram_register_types(void) { type_register_static(&nvram_sysbus_info); } -device_init(nvram_register) +type_init(nvram_register_types) diff --git a/hw/ds1338.c b/hw/ds1338.c index b137e13379..6397f0aa6f 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -135,9 +135,9 @@ static TypeInfo ds1338_info = { .class_init = ds1338_class_init, }; -static void ds1338_register_devices(void) +static void ds1338_register_types(void) { type_register_static(&ds1338_info); } -device_init(ds1338_register_devices) +type_init(ds1338_register_types) diff --git a/hw/e1000.c b/hw/e1000.c index 751f79d5ff..7babc0b06e 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -1227,9 +1227,9 @@ static TypeInfo e1000_info = { .class_init = e1000_class_init, }; -static void e1000_register_devices(void) +static void e1000_register_types(void) { type_register_static(&e1000_info); } -device_init(e1000_register_devices) +type_init(e1000_register_types) diff --git a/hw/eccmemctl.c b/hw/eccmemctl.c index 1cf2090c1f..fe1cd90007 100644 --- a/hw/eccmemctl.c +++ b/hw/eccmemctl.c @@ -332,9 +332,9 @@ static TypeInfo ecc_info = { }; -static void ecc_register_devices(void) +static void ecc_register_types(void) { type_register_static(&ecc_info); } -device_init(ecc_register_devices) +type_init(ecc_register_types) diff --git a/hw/eepro100.c b/hw/eepro100.c index 843610c933..e3ba71974e 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -2089,7 +2089,7 @@ static void eepro100_class_init(ObjectClass *klass, void *data) k->subsystem_id = info->subsystem_id; } -static void eepro100_register_devices(void) +static void eepro100_register_types(void) { size_t i; for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { @@ -2105,4 +2105,4 @@ static void eepro100_register_devices(void) } } -device_init(eepro100_register_devices) +type_init(eepro100_register_types) diff --git a/hw/empty_slot.c b/hw/empty_slot.c index 1bc181555f..099c85e583 100644 --- a/hw/empty_slot.c +++ b/hw/empty_slot.c @@ -90,9 +90,9 @@ static TypeInfo empty_slot_info = { .class_init = empty_slot_class_init, }; -static void empty_slot_register_devices(void) +static void empty_slot_register_types(void) { type_register_static(&empty_slot_info); } -device_init(empty_slot_register_devices); +type_init(empty_slot_register_types) diff --git a/hw/es1370.c b/hw/es1370.c index e377c48e49..f19cef31a6 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -1054,9 +1054,10 @@ static TypeInfo es1370_info = { .class_init = es1370_class_init, }; -static void es1370_register (void) +static void es1370_register_types (void) { type_register_static (&es1370_info); } -device_init (es1370_register); + +type_init (es1370_register_types) @@ -931,9 +931,9 @@ static TypeInfo escc_info = { .class_init = escc_class_init, }; -static void escc_register_devices(void) +static void escc_register_types(void) { type_register_static(&escc_info); } -device_init(escc_register_devices) +type_init(escc_register_types) @@ -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); @@ -775,9 +776,9 @@ static TypeInfo esp_info = { .class_init = esp_class_init, }; -static void esp_register_devices(void) +static void esp_register_types(void) { type_register_static(&esp_info); } -device_init(esp_register_devices) +type_init(esp_register_types) diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index aefd5778ab..16a0637a4a 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -637,9 +637,9 @@ static TypeInfo etraxfs_eth_info = { .class_init = etraxfs_eth_class_init, }; -static void etraxfs_eth_register(void) +static void etraxfs_eth_register_types(void) { type_register_static(&etraxfs_eth_info); } -device_init(etraxfs_eth_register) +type_init(etraxfs_eth_register_types) diff --git a/hw/etraxfs_pic.c b/hw/etraxfs_pic.c index 33541fcc61..dc27f88ac9 100644 --- a/hw/etraxfs_pic.c +++ b/hw/etraxfs_pic.c @@ -172,9 +172,9 @@ static TypeInfo etraxfs_pic_info = { .class_init = etraxfs_pic_class_init, }; -static void etraxfs_pic_register(void) +static void etraxfs_pic_register_types(void) { type_register_static(&etraxfs_pic_info); } -device_init(etraxfs_pic_register) +type_init(etraxfs_pic_register_types) diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c index 567cb8cc2d..cecd819584 100644 --- a/hw/etraxfs_ser.c +++ b/hw/etraxfs_ser.c @@ -240,9 +240,9 @@ static TypeInfo etraxfs_ser_info = { .class_init = etraxfs_ser_class_init, }; -static void etraxfs_serial_register(void) +static void etraxfs_serial_register_types(void) { type_register_static(&etraxfs_ser_info); } -device_init(etraxfs_serial_register) +type_init(etraxfs_serial_register_types) diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c index b71c5ee9d5..9076a49884 100644 --- a/hw/etraxfs_timer.c +++ b/hw/etraxfs_timer.c @@ -343,9 +343,9 @@ static TypeInfo etraxfs_timer_info = { .class_init = etraxfs_timer_class_init, }; -static void etraxfs_timer_register(void) +static void etraxfs_timer_register_types(void) { type_register_static(&etraxfs_timer_info); } -device_init(etraxfs_timer_register) +type_init(etraxfs_timer_register_types) diff --git a/hw/exynos4210.c b/hw/exynos4210.c new file mode 100644 index 0000000000..f904370505 --- /dev/null +++ b/hw/exynos4210.c @@ -0,0 +1,270 @@ +/* + * Samsung exynos4210 SoC emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * Maksim Kozlov <m.kozlov@samsung.com> + * Evgeny Voevodin <e.voevodin@samsung.com> + * Igor Mitsyanko <i.mitsyanko@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "boards.h" +#include "sysemu.h" +#include "sysbus.h" +#include "arm-misc.h" +#include "exynos4210.h" + +#define EXYNOS4210_CHIPID_ADDR 0x10000000 + +/* PWM */ +#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000 + +/* MCT */ +#define EXYNOS4210_MCT_BASE_ADDR 0x10050000 + +/* UART's definitions */ +#define EXYNOS4210_UART0_BASE_ADDR 0x13800000 +#define EXYNOS4210_UART1_BASE_ADDR 0x13810000 +#define EXYNOS4210_UART2_BASE_ADDR 0x13820000 +#define EXYNOS4210_UART3_BASE_ADDR 0x13830000 +#define EXYNOS4210_UART0_FIFO_SIZE 256 +#define EXYNOS4210_UART1_FIFO_SIZE 64 +#define EXYNOS4210_UART2_FIFO_SIZE 16 +#define EXYNOS4210_UART3_FIFO_SIZE 16 +/* Interrupt Group of External Interrupt Combiner for UART */ +#define EXYNOS4210_UART_INT_GRP 26 + +/* External GIC */ +#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR 0x10480000 +#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR 0x10490000 + +/* Combiner */ +#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000 +#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000 + +/* PMU SFR base address */ +#define EXYNOS4210_PMU_BASE_ADDR 0x10020000 + +/* Display controllers (FIMD) */ +#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000 + +static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43, + 0x09, 0x00, 0x00, 0x00 }; + +Exynos4210State *exynos4210_init(MemoryRegion *system_mem, + unsigned long ram_size) +{ + qemu_irq cpu_irq[4]; + int n; + Exynos4210State *s = g_new(Exynos4210State, 1); + qemu_irq *irqp; + qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS]; + unsigned long mem_size; + DeviceState *dev; + SysBusDevice *busdev; + + for (n = 0; n < EXYNOS4210_NCPUS; n++) { + s->env[n] = cpu_init("cortex-a9"); + if (!s->env[n]) { + fprintf(stderr, "Unable to find CPU %d definition\n", n); + exit(1); + } + /* Create PIC controller for each processor instance */ + irqp = arm_pic_init_cpu(s->env[n]); + + /* + * Get GICs gpio_in cpu_irq to connect a combiner to them later. + * Use only IRQ for a while. + */ + cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ]; + } + + /*** IRQs ***/ + + s->irq_table = exynos4210_init_irq(&s->irqs); + + /* IRQ Gate */ + dev = qdev_create(NULL, "exynos4210.irq_gate"); + qdev_init_nofail(dev); + /* Get IRQ Gate input in gate_irq */ + for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { + gate_irq[n] = qdev_get_gpio_in(dev, n); + } + busdev = sysbus_from_qdev(dev); + /* Connect IRQ Gate output to cpu_irq */ + for (n = 0; n < EXYNOS4210_NCPUS; n++) { + sysbus_connect_irq(busdev, n, cpu_irq[n]); + } + + /* Private memory region and Internal GIC */ + dev = qdev_create(NULL, "a9mpcore_priv"); + qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); + for (n = 0; n < EXYNOS4210_NCPUS; n++) { + sysbus_connect_irq(busdev, n, gate_irq[n * 2]); + } + for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { + s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); + } + + /* Cache controller */ + sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL); + + /* External GIC */ + dev = qdev_create(NULL, "exynos4210.gic"); + qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + /* Map CPU interface */ + sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR); + /* Map Distributer interface */ + sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); + for (n = 0; n < EXYNOS4210_NCPUS; n++) { + sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]); + } + for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { + s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); + } + + /* Internal Interrupt Combiner */ + dev = qdev_create(NULL, "exynos4210.combiner"); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { + sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]); + } + exynos4210_combiner_get_gpioin(&s->irqs, dev, 0); + sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR); + + /* External Interrupt Combiner */ + dev = qdev_create(NULL, "exynos4210.combiner"); + qdev_prop_set_uint32(dev, "external", 1); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { + sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]); + } + exynos4210_combiner_get_gpioin(&s->irqs, dev, 1); + sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR); + + /* Initialize board IRQs. */ + exynos4210_init_board_irqs(&s->irqs); + + /*** Memory ***/ + + /* Chip-ID and OMR */ + memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid", + sizeof(chipid_and_omr), chipid_and_omr); + memory_region_set_readonly(&s->chipid_mem, true); + memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR, + &s->chipid_mem); + + /* Internal ROM */ + memory_region_init_ram(&s->irom_mem, "exynos4210.irom", + EXYNOS4210_IROM_SIZE); + memory_region_set_readonly(&s->irom_mem, true); + memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR, + &s->irom_mem); + /* mirror of iROM */ + memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias", + &s->irom_mem, + EXYNOS4210_IROM_BASE_ADDR, + EXYNOS4210_IROM_SIZE); + memory_region_set_readonly(&s->irom_alias_mem, true); + memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR, + &s->irom_alias_mem); + + /* Internal RAM */ + memory_region_init_ram(&s->iram_mem, "exynos4210.iram", + EXYNOS4210_IRAM_SIZE); + vmstate_register_ram_global(&s->iram_mem); + memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR, + &s->iram_mem); + + /* DRAM */ + mem_size = ram_size; + if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) { + memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1", + mem_size - EXYNOS4210_DRAM_MAX_SIZE); + vmstate_register_ram_global(&s->dram1_mem); + memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR, + &s->dram1_mem); + mem_size = EXYNOS4210_DRAM_MAX_SIZE; + } + memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size); + vmstate_register_ram_global(&s->dram0_mem); + memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR, + &s->dram0_mem); + + /* PMU. + * The only reason of existence at the moment is that secondary CPU boot + * loader uses PMU INFORM5 register as a holding pen. + */ + sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL); + + /* PWM */ + sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR, + s->irq_table[exynos4210_get_irq(22, 0)], + s->irq_table[exynos4210_get_irq(22, 1)], + s->irq_table[exynos4210_get_irq(22, 2)], + s->irq_table[exynos4210_get_irq(22, 3)], + s->irq_table[exynos4210_get_irq(22, 4)], + NULL); + + /* Multi Core Timer */ + dev = qdev_create(NULL, "exynos4210.mct"); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + for (n = 0; n < 4; n++) { + /* Connect global timer interrupts to Combiner gpio_in */ + sysbus_connect_irq(busdev, n, + s->irq_table[exynos4210_get_irq(1, 4 + n)]); + } + /* Connect local timer interrupts to Combiner gpio_in */ + sysbus_connect_irq(busdev, 4, + s->irq_table[exynos4210_get_irq(51, 0)]); + sysbus_connect_irq(busdev, 5, + s->irq_table[exynos4210_get_irq(35, 3)]); + sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR); + + /*** UARTs ***/ + exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, + EXYNOS4210_UART0_FIFO_SIZE, 0, NULL, + s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]); + + exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR, + EXYNOS4210_UART1_FIFO_SIZE, 1, NULL, + s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]); + + exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR, + EXYNOS4210_UART2_FIFO_SIZE, 2, NULL, + s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]); + + exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR, + EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, + s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); + + /*** Display controller (FIMD) ***/ + sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR, + s->irq_table[exynos4210_get_irq(11, 0)], + s->irq_table[exynos4210_get_irq(11, 1)], + s->irq_table[exynos4210_get_irq(11, 2)], + NULL); + + return s; +} diff --git a/hw/exynos4210.h b/hw/exynos4210.h new file mode 100644 index 0000000000..e7522f851a --- /dev/null +++ b/hw/exynos4210.h @@ -0,0 +1,131 @@ +/* + * Samsung exynos4210 SoC emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * Maksim Kozlov <m.kozlov@samsung.com> + * Evgeny Voevodin <e.voevodin@samsung.com> + * Igor Mitsyanko <i.mitsyanko@samsung.com> + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifndef EXYNOS4210_H_ +#define EXYNOS4210_H_ + +#include "qemu-common.h" +#include "memory.h" + +#define EXYNOS4210_NCPUS 2 + +#define EXYNOS4210_DRAM0_BASE_ADDR 0x40000000 +#define EXYNOS4210_DRAM1_BASE_ADDR 0xa0000000 +#define EXYNOS4210_DRAM_MAX_SIZE 0x60000000 /* 1.5 GB */ + +#define EXYNOS4210_IROM_BASE_ADDR 0x00000000 +#define EXYNOS4210_IROM_SIZE 0x00010000 /* 64 KB */ +#define EXYNOS4210_IROM_MIRROR_BASE_ADDR 0x02000000 +#define EXYNOS4210_IROM_MIRROR_SIZE 0x00010000 /* 64 KB */ + +#define EXYNOS4210_IRAM_BASE_ADDR 0x02020000 +#define EXYNOS4210_IRAM_SIZE 0x00020000 /* 128 KB */ + +/* Secondary CPU startup code is in IROM memory */ +#define EXYNOS4210_SMP_BOOT_ADDR EXYNOS4210_IROM_BASE_ADDR +#define EXYNOS4210_SMP_BOOT_SIZE 0x1000 +#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR +/* Secondary CPU polling address to get loader start from */ +#define EXYNOS4210_SECOND_CPU_BOOTREG 0x10020814 + +#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000 +#define EXYNOS4210_L2X0_BASE_ADDR 0x10502000 + +/* + * exynos4210 IRQ subsystem stub definitions. + */ +#define EXYNOS4210_IRQ_GATE_NINPUTS 8 + +#define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64 +#define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16 +#define EXYNOS4210_MAX_INT_COMBINER_IN_IRQ \ + (EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ * 8) +#define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ \ + (EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8) + +#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp)*8 + (bit)) +#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8) +#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \ + ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq)) + +/* IRQs number for external and internal GIC */ +#define EXYNOS4210_EXT_GIC_NIRQ (160-32) +#define EXYNOS4210_INT_GIC_NIRQ 64 + +typedef struct Exynos4210Irq { + qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; + qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; + qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ]; + qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ]; + qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; +} Exynos4210Irq; + +typedef struct Exynos4210State { + CPUState * env[EXYNOS4210_NCPUS]; + Exynos4210Irq irqs; + qemu_irq *irq_table; + + MemoryRegion chipid_mem; + MemoryRegion iram_mem; + MemoryRegion irom_mem; + MemoryRegion irom_alias_mem; + MemoryRegion dram0_mem; + MemoryRegion dram1_mem; + MemoryRegion boot_secondary; + MemoryRegion bootreg_mem; +} Exynos4210State; + +Exynos4210State *exynos4210_init(MemoryRegion *system_mem, + unsigned long ram_size); + +/* Initialize exynos4210 IRQ subsystem stub */ +qemu_irq *exynos4210_init_irq(Exynos4210Irq *env); + +/* Initialize board IRQs. + * These IRQs contain splitted Int/External Combiner and External Gic IRQs */ +void exynos4210_init_board_irqs(Exynos4210Irq *s); + +/* Get IRQ number from exynos4210 IRQ subsystem stub. + * To identify IRQ source use internal combiner group and bit number + * grp - group number + * bit - bit number inside group */ +uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit); + +/* + * Get Combiner input GPIO into irqs structure + */ +void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, + int ext); + +/* + * exynos4210 UART + */ +DeviceState *exynos4210_uart_create(target_phys_addr_t addr, + int fifo_size, + int channel, + CharDriverState *chr, + qemu_irq irq); + +#endif /* EXYNOS4210_H_ */ diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c new file mode 100644 index 0000000000..6110c19d5d --- /dev/null +++ b/hw/exynos4210_combiner.c @@ -0,0 +1,469 @@ +/* + * Samsung exynos4210 Interrupt Combiner + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin <e.voevodin@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines + * IRQ sources into groups and provides signal output to GIC from each group. It + * is driven by common mask and enable/disable logic. Take a note that not all + * IRQs are passed to GIC through Combiner. + */ + +#include "sysbus.h" + +#include "exynos4210.h" + +//#define DEBUG_COMBINER + +#ifdef DEBUG_COMBINER +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define IIC_NGRP 64 /* Internal Interrupt Combiner + Groups number */ +#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner + Interrupts number */ +#define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ +#define IIC_REGSET_SIZE 0x41 + +/* + * State for each output signal of internal combiner + */ +typedef struct CombinerGroupState { + uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ + uint8_t src_pending; /* Pending source interrupts before masking */ +} CombinerGroupState; + +typedef struct Exynos4210CombinerState { + SysBusDevice busdev; + MemoryRegion iomem; + + struct CombinerGroupState group[IIC_NGRP]; + uint32_t reg_set[IIC_REGSET_SIZE]; + uint32_t icipsr[2]; + uint32_t external; /* 1 means that this combiner is external */ + + qemu_irq output_irq[IIC_NGRP]; +} Exynos4210CombinerState; + +static const VMStateDescription vmstate_exynos4210_combiner_group_state = { + .name = "exynos4210.combiner.groupstate", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(src_mask, CombinerGroupState), + VMSTATE_UINT8(src_pending, CombinerGroupState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_combiner = { + .name = "exynos4210.combiner", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, + vmstate_exynos4210_combiner_group_state, CombinerGroupState), + VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, + IIC_REGSET_SIZE), + VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), + VMSTATE_UINT32(external, Exynos4210CombinerState), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Get Combiner input GPIO into irqs structure + */ +void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, + int ext) +{ + int n; + int bit; + int max; + qemu_irq *irq; + + max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : + EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; + irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; + + /* + * Some IRQs of Int/External Combiner are going to two Combiners groups, + * so let split them. + */ + for (n = 0; n < max; n++) { + + bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); + + switch (n) { + /* MDNIE_LCD1 INTG1 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); + continue; + + /* TMU INTG3 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); + continue; + + /* LCD1 INTG12 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); + continue; + + /* Multi-Core Timer INTG12 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); + continue; + + /* Multi-Core Timer INTG35 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); + continue; + + /* Multi-Core Timer INTG51 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); + continue; + + /* Multi-Core Timer INTG53 */ + case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... + EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): + irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), + irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); + continue; + } + + irq[n] = qdev_get_gpio_in(dev, n); + } +} + +static uint64_t +exynos4210_combiner_read(void *opaque, target_phys_addr_t offset, unsigned size) +{ + struct Exynos4210CombinerState *s = + (struct Exynos4210CombinerState *)opaque; + uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and + get a start of corresponding group quad */ + uint32_t grp_quad_base_n; /* Base of group quad */ + uint32_t reg_n; /* Register number inside the quad */ + uint32_t val; + + if (s->external && (offset > 0x3c && offset != 0x100)) { + hw_error("exynos4210.combiner: unallowed read access at offset 0x" + TARGET_FMT_plx "\n", offset); + } + + req_quad_base_n = offset >> 4; + grp_quad_base_n = req_quad_base_n << 2; + reg_n = (offset - (req_quad_base_n << 4)) >> 2; + + if (req_quad_base_n >= IIC_NGRP) { + /* Read of ICIPSR register */ + return s->icipsr[reg_n]; + } + + val = 0; + + switch (reg_n) { + /* IISTR */ + case 2: + val |= s->group[grp_quad_base_n].src_pending; + val |= s->group[grp_quad_base_n + 1].src_pending << 8; + val |= s->group[grp_quad_base_n + 2].src_pending << 16; + val |= s->group[grp_quad_base_n + 3].src_pending << 24; + break; + /* IIMSR */ + case 3: + val |= s->group[grp_quad_base_n].src_mask & + s->group[grp_quad_base_n].src_pending; + val |= (s->group[grp_quad_base_n + 1].src_mask & + s->group[grp_quad_base_n + 1].src_pending) << 8; + val |= (s->group[grp_quad_base_n + 2].src_mask & + s->group[grp_quad_base_n + 2].src_pending) << 16; + val |= (s->group[grp_quad_base_n + 3].src_mask & + s->group[grp_quad_base_n + 3].src_pending) << 24; + break; + default: + if (offset >> 2 >= IIC_REGSET_SIZE) { + hw_error("exynos4210.combiner: overflow of reg_set by 0x" + TARGET_FMT_plx "offset\n", offset); + } + val = s->reg_set[offset >> 2]; + return 0; + } + return val; +} + +static void exynos4210_combiner_update(void *opaque, uint8_t group_n) +{ + struct Exynos4210CombinerState *s = + (struct Exynos4210CombinerState *)opaque; + + /* Send interrupt if needed */ + if (s->group[group_n].src_mask & s->group[group_n].src_pending) { +#ifdef DEBUG_COMBINER + if (group_n != 26) { + /* skip uart */ + DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); + } +#endif + + /* Set Combiner interrupt pending status after masking */ + if (group_n >= 32) { + s->icipsr[1] |= 1 << (group_n - 32); + } else { + s->icipsr[0] |= 1 << group_n; + } + + qemu_irq_raise(s->output_irq[group_n]); + } else { +#ifdef DEBUG_COMBINER + if (group_n != 26) { + /* skip uart */ + DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); + } +#endif + + /* Set Combiner interrupt pending status after masking */ + if (group_n >= 32) { + s->icipsr[1] &= ~(1 << (group_n - 32)); + } else { + s->icipsr[0] &= ~(1 << group_n); + } + + qemu_irq_lower(s->output_irq[group_n]); + } +} + +static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + struct Exynos4210CombinerState *s = + (struct Exynos4210CombinerState *)opaque; + uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and + get a start of corresponding group quad */ + uint32_t grp_quad_base_n; /* Base of group quad */ + uint32_t reg_n; /* Register number inside the quad */ + + if (s->external && (offset > 0x3c && offset != 0x100)) { + hw_error("exynos4210.combiner: unallowed write access at offset 0x" + TARGET_FMT_plx "\n", offset); + } + + req_quad_base_n = offset >> 4; + grp_quad_base_n = req_quad_base_n << 2; + reg_n = (offset - (req_quad_base_n << 4)) >> 2; + + if (req_quad_base_n >= IIC_NGRP) { + hw_error("exynos4210.combiner: unallowed write access at offset 0x" + TARGET_FMT_plx "\n", offset); + return; + } + + if (reg_n > 1) { + hw_error("exynos4210.combiner: unallowed write access at offset 0x" + TARGET_FMT_plx "\n", offset); + return; + } + + if (offset >> 2 >= IIC_REGSET_SIZE) { + hw_error("exynos4210.combiner: overflow of reg_set by 0x" + TARGET_FMT_plx "offset\n", offset); + } + s->reg_set[offset >> 2] = val; + + switch (reg_n) { + /* IIESR */ + case 0: + /* FIXME: what if irq is pending, allowed by mask, and we allow it + * again. Interrupt will rise again! */ + + DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", + s->external ? "EXT" : "INT", + grp_quad_base_n, + grp_quad_base_n + 1, + grp_quad_base_n + 2, + grp_quad_base_n + 3); + + /* Enable interrupt sources */ + s->group[grp_quad_base_n].src_mask |= val & 0xFF; + s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; + s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; + s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; + + exynos4210_combiner_update(s, grp_quad_base_n); + exynos4210_combiner_update(s, grp_quad_base_n + 1); + exynos4210_combiner_update(s, grp_quad_base_n + 2); + exynos4210_combiner_update(s, grp_quad_base_n + 3); + break; + /* IIECR */ + case 1: + DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", + s->external ? "EXT" : "INT", + grp_quad_base_n, + grp_quad_base_n + 1, + grp_quad_base_n + 2, + grp_quad_base_n + 3); + + /* Disable interrupt sources */ + s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); + s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); + s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); + s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); + + exynos4210_combiner_update(s, grp_quad_base_n); + exynos4210_combiner_update(s, grp_quad_base_n + 1); + exynos4210_combiner_update(s, grp_quad_base_n + 2); + exynos4210_combiner_update(s, grp_quad_base_n + 3); + break; + default: + hw_error("exynos4210.combiner: unallowed write access at offset 0x" + TARGET_FMT_plx "\n", offset); + break; + } + + return; +} + +/* Get combiner group and bit from irq number */ +static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) +{ + *bit = irq - ((irq >> 3) << 3); + return irq >> 3; +} + +/* Process a change in an external IRQ input. */ +static void exynos4210_combiner_handler(void *opaque, int irq, int level) +{ + struct Exynos4210CombinerState *s = + (struct Exynos4210CombinerState *)opaque; + uint8_t bit_n, group_n; + + group_n = get_combiner_group_and_bit(irq, &bit_n); + + if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { + DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" + , group_n); + return; + } + + if (level) { + s->group[group_n].src_pending |= 1 << bit_n; + } else { + s->group[group_n].src_pending &= ~(1 << bit_n); + } + + exynos4210_combiner_update(s, group_n); + + return; +} + +static void exynos4210_combiner_reset(DeviceState *d) +{ + struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; + + memset(&s->group, 0, sizeof(s->group)); + memset(&s->reg_set, 0, sizeof(s->reg_set)); + + s->reg_set[0xC0 >> 2] = 0x01010101; + s->reg_set[0xC4 >> 2] = 0x01010101; + s->reg_set[0xD0 >> 2] = 0x01010101; + s->reg_set[0xD4 >> 2] = 0x01010101; +} + +static const MemoryRegionOps exynos4210_combiner_ops = { + .read = exynos4210_combiner_read, + .write = exynos4210_combiner_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* + * Internal Combiner initialization. + */ +static int exynos4210_combiner_init(SysBusDevice *dev) +{ + unsigned int i; + struct Exynos4210CombinerState *s = + FROM_SYSBUS(struct Exynos4210CombinerState, dev); + + /* Allocate general purpose input signals and connect a handler to each of + * them */ + qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ); + + /* Connect SysBusDev irqs to device specific irqs */ + for (i = 0; i < IIC_NIRQ; i++) { + sysbus_init_irq(dev, &s->output_irq[i]); + } + + memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s, + "exynos4210-combiner", IIC_REGION_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static Property exynos4210_combiner_properties[] = { + DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_combiner_init; + dc->reset = exynos4210_combiner_reset; + dc->props = exynos4210_combiner_properties; + dc->vmsd = &vmstate_exynos4210_combiner; +} + +static TypeInfo exynos4210_combiner_info = { + .name = "exynos4210.combiner", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210CombinerState), + .class_init = exynos4210_combiner_class_init, +}; + +static void exynos4210_combiner_register_types(void) +{ + type_register_static(&exynos4210_combiner_info); +} + +type_init(exynos4210_combiner_register_types) diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c new file mode 100644 index 0000000000..3313f00a71 --- /dev/null +++ b/hw/exynos4210_fimd.c @@ -0,0 +1,1928 @@ +/* + * Samsung exynos4210 Display Controller (FIMD) + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * Based on LCD controller for Samsung S5PC1xx-based board emulation + * by Kirill Batuzov <batuzovk@ispras.ru> + * + * Contributed by Mitsyanko Igor <i.mitsyanko@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu-common.h" +#include "cpu-all.h" +#include "sysbus.h" +#include "console.h" +#include "pixel_ops.h" +#include "bswap.h" + +/* Debug messages configuration */ +#define EXYNOS4210_FIMD_DEBUG 0 +#define EXYNOS4210_FIMD_MODE_TRACE 0 + +#if EXYNOS4210_FIMD_DEBUG == 0 + #define DPRINT_L1(fmt, args...) do { } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define DPRINT_ERROR(fmt, args...) do { } while (0) +#elif EXYNOS4210_FIMD_DEBUG == 1 + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define DPRINT_ERROR(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) +#else + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_ERROR(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) +#endif + +#if EXYNOS4210_FIMD_MODE_TRACE == 0 + #define DPRINT_TRACE(fmt, args...) do { } while (0) +#else + #define DPRINT_TRACE(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) +#endif + +#define NUM_OF_WINDOWS 5 +#define FIMD_REGS_SIZE 0x4114 + +/* Video main control registers */ +#define FIMD_VIDCON0 0x0000 +#define FIMD_VIDCON1 0x0004 +#define FIMD_VIDCON2 0x0008 +#define FIMD_VIDCON3 0x000C +#define FIMD_VIDCON0_ENVID_F (1 << 0) +#define FIMD_VIDCON0_ENVID (1 << 1) +#define FIMD_VIDCON0_ENVID_MASK ((1 << 0) | (1 << 1)) +#define FIMD_VIDCON1_ROMASK 0x07FFE000 + +/* Video time control registers */ +#define FIMD_VIDTCON_START 0x10 +#define FIMD_VIDTCON_END 0x1C +#define FIMD_VIDTCON2_SIZE_MASK 0x07FF +#define FIMD_VIDTCON2_HOR_SHIFT 0 +#define FIMD_VIDTCON2_VER_SHIFT 11 + +/* Window control registers */ +#define FIMD_WINCON_START 0x0020 +#define FIMD_WINCON_END 0x0030 +#define FIMD_WINCON_ROMASK 0x82200000 +#define FIMD_WINCON_ENWIN (1 << 0) +#define FIMD_WINCON_BLD_PIX (1 << 6) +#define FIMD_WINCON_ALPHA_MUL (1 << 7) +#define FIMD_WINCON_ALPHA_SEL (1 << 1) +#define FIMD_WINCON_SWAP 0x078000 +#define FIMD_WINCON_SWAP_SHIFT 15 +#define FIMD_WINCON_SWAP_WORD 0x1 +#define FIMD_WINCON_SWAP_HWORD 0x2 +#define FIMD_WINCON_SWAP_BYTE 0x4 +#define FIMD_WINCON_SWAP_BITS 0x8 +#define FIMD_WINCON_BUFSTAT_L (1 << 21) +#define FIMD_WINCON_BUFSTAT_H (1 << 31) +#define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31)) +#define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31)) +#define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31)) +#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31)) +#define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30)) +#define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30)) +#define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30)) +#define FIMD_WINCON_BUF2_SEL ((0 << 20) | (1 << 30)) +#define FIMD_WINCON_BUFMODE (1 << 14) +#define IS_PALETTIZED_MODE(w) (w->wincon & 0xC) +#define PAL_MODE_WITH_ALPHA(x) ((x) == 7) +#define WIN_BPP_MODE(w) ((w->wincon >> 2) & 0xF) +#define WIN_BPP_MODE_WITH_ALPHA(w) \ + (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE) + +/* Shadow control register */ +#define FIMD_SHADOWCON 0x0034 +#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w)))) +/* Channel mapping control register */ +#define FIMD_WINCHMAP 0x003C + +/* Window position control registers */ +#define FIMD_VIDOSD_START 0x0040 +#define FIMD_VIDOSD_END 0x0088 +#define FIMD_VIDOSD_COORD_MASK 0x07FF +#define FIMD_VIDOSD_HOR_SHIFT 11 +#define FIMD_VIDOSD_VER_SHIFT 0 +#define FIMD_VIDOSD_ALPHA_AEN0 0xFFF000 +#define FIMD_VIDOSD_AEN0_SHIFT 12 +#define FIMD_VIDOSD_ALPHA_AEN1 0x000FFF + +/* Frame buffer address registers */ +#define FIMD_VIDWADD0_START 0x00A0 +#define FIMD_VIDWADD0_END 0x00C4 +#define FIMD_VIDWADD0_END 0x00C4 +#define FIMD_VIDWADD1_START 0x00D0 +#define FIMD_VIDWADD1_END 0x00F4 +#define FIMD_VIDWADD2_START 0x0100 +#define FIMD_VIDWADD2_END 0x0110 +#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF +#define FIMD_VIDWADD2_OFFSIZE 0x1FFF +#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13 +#define FIMD_VIDW0ADD0_B2 0x20A0 +#define FIMD_VIDW4ADD0_B2 0x20C0 + +/* Video interrupt control registers */ +#define FIMD_VIDINTCON0 0x130 +#define FIMD_VIDINTCON1 0x134 + +/* Window color key registers */ +#define FIMD_WKEYCON_START 0x140 +#define FIMD_WKEYCON_END 0x15C +#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF +#define FIMD_WKEYCON0_CTL_SHIFT 24 +#define FIMD_WKEYCON0_DIRCON (1 << 24) +#define FIMD_WKEYCON0_KEYEN (1 << 25) +#define FIMD_WKEYCON0_KEYBLEN (1 << 26) +/* Window color key alpha control register */ +#define FIMD_WKEYALPHA_START 0x160 +#define FIMD_WKEYALPHA_END 0x16C + +/* Dithering control register */ +#define FIMD_DITHMODE 0x170 + +/* Window alpha control registers */ +#define FIMD_VIDALPHA_ALPHA_LOWER 0x000F0F0F +#define FIMD_VIDALPHA_ALPHA_UPPER 0x00F0F0F0 +#define FIMD_VIDWALPHA_START 0x21C +#define FIMD_VIDWALPHA_END 0x240 + +/* Window color map registers */ +#define FIMD_WINMAP_START 0x180 +#define FIMD_WINMAP_END 0x190 +#define FIMD_WINMAP_EN (1 << 24) +#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF + +/* Window palette control registers */ +#define FIMD_WPALCON_HIGH 0x019C +#define FIMD_WPALCON_LOW 0x01A0 +#define FIMD_WPALCON_UPDATEEN (1 << 9) +#define FIMD_WPAL_W0PAL_L 0x07 +#define FIMD_WPAL_W0PAL_L_SHT 0 +#define FIMD_WPAL_W1PAL_L 0x07 +#define FIMD_WPAL_W1PAL_L_SHT 3 +#define FIMD_WPAL_W2PAL_L 0x01 +#define FIMD_WPAL_W2PAL_L_SHT 6 +#define FIMD_WPAL_W2PAL_H 0x06 +#define FIMD_WPAL_W2PAL_H_SHT 8 +#define FIMD_WPAL_W3PAL_L 0x01 +#define FIMD_WPAL_W3PAL_L_SHT 7 +#define FIMD_WPAL_W3PAL_H 0x06 +#define FIMD_WPAL_W3PAL_H_SHT 12 +#define FIMD_WPAL_W4PAL_L 0x01 +#define FIMD_WPAL_W4PAL_L_SHT 8 +#define FIMD_WPAL_W4PAL_H 0x06 +#define FIMD_WPAL_W4PAL_H_SHT 16 + +/* Trigger control registers */ +#define FIMD_TRIGCON 0x01A4 +#define FIMD_TRIGCON_ROMASK 0x00000004 + +/* LCD I80 Interface Control */ +#define FIMD_I80IFCON_START 0x01B0 +#define FIMD_I80IFCON_END 0x01BC +/* Color gain control register */ +#define FIMD_COLORGAINCON 0x01C0 +/* LCD i80 Interface Command Control */ +#define FIMD_LDI_CMDCON0 0x01D0 +#define FIMD_LDI_CMDCON1 0x01D4 +/* I80 System Interface Manual Command Control */ +#define FIMD_SIFCCON0 0x01E0 +#define FIMD_SIFCCON2 0x01E8 + +/* Hue Control Registers */ +#define FIMD_HUECOEFCR_START 0x01EC +#define FIMD_HUECOEFCR_END 0x01F4 +#define FIMD_HUECOEFCB_START 0x01FC +#define FIMD_HUECOEFCB_END 0x0208 +#define FIMD_HUEOFFSET 0x020C + +/* Video interrupt control registers */ +#define FIMD_VIDINT_INTFIFOPEND (1 << 0) +#define FIMD_VIDINT_INTFRMPEND (1 << 1) +#define FIMD_VIDINT_INTI80PEND (1 << 2) +#define FIMD_VIDINT_INTEN (1 << 0) +#define FIMD_VIDINT_INTFIFOEN (1 << 1) +#define FIMD_VIDINT_INTFRMEN (1 << 12) +#define FIMD_VIDINT_I80IFDONE (1 << 17) + +/* Window blend equation control registers */ +#define FIMD_BLENDEQ_START 0x0244 +#define FIMD_BLENDEQ_END 0x0250 +#define FIMD_BLENDCON 0x0260 +#define FIMD_ALPHA_8BIT (1 << 0) +#define FIMD_BLENDEQ_COEF_MASK 0xF + +/* Window RTQOS Control Registers */ +#define FIMD_WRTQOSCON_START 0x0264 +#define FIMD_WRTQOSCON_END 0x0274 + +/* LCD I80 Interface Command */ +#define FIMD_I80IFCMD_START 0x0280 +#define FIMD_I80IFCMD_END 0x02AC + +/* Shadow windows control registers */ +#define FIMD_SHD_ADD0_START 0x40A0 +#define FIMD_SHD_ADD0_END 0x40C0 +#define FIMD_SHD_ADD1_START 0x40D0 +#define FIMD_SHD_ADD1_END 0x40F0 +#define FIMD_SHD_ADD2_START 0x4100 +#define FIMD_SHD_ADD2_END 0x4110 + +/* Palette memory */ +#define FIMD_PAL_MEM_START 0x2400 +#define FIMD_PAL_MEM_END 0x37FC +/* Palette memory aliases for windows 0 and 1 */ +#define FIMD_PALMEM_AL_START 0x0400 +#define FIMD_PALMEM_AL_END 0x0BFC + +typedef struct { + uint8_t r, g, b; + /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */ + uint32_t a; +} rgba; +#define RGBA_SIZE 7 + +typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p); +typedef struct Exynos4210fimdWindow Exynos4210fimdWindow; + +struct Exynos4210fimdWindow { + uint32_t wincon; /* Window control register */ + uint32_t buf_start[3]; /* Start address for video frame buffer */ + uint32_t buf_end[3]; /* End address for video frame buffer */ + uint32_t keycon[2]; /* Window color key registers */ + uint32_t keyalpha; /* Color key alpha control register */ + uint32_t winmap; /* Window color map register */ + uint32_t blendeq; /* Window blending equation control register */ + uint32_t rtqoscon; /* Window RTQOS Control Registers */ + uint32_t palette[256]; /* Palette RAM */ + uint32_t shadow_buf_start; /* Start address of shadow frame buffer */ + uint32_t shadow_buf_end; /* End address of shadow frame buffer */ + uint32_t shadow_buf_size; /* Virtual shadow screen width */ + + pixel_to_rgb_func *pixel_to_rgb; + void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, + bool blend); + uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); + uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ + uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ + uint32_t osdsize; /* VIDOSD2&3 register */ + uint32_t alpha_val[2]; /* VIDOSD2&3, VIDWALPHA registers */ + uint16_t virtpage_width; /* VIDWADD2 register */ + uint16_t virtpage_offsize; /* VIDWADD2 register */ + MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */ + uint8_t *host_fb_addr; /* Host pointer to window's framebuffer */ + target_phys_addr_t fb_len; /* Framebuffer length */ +}; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + DisplayState *console; + qemu_irq irq[3]; + + uint32_t vidcon[4]; /* Video main control registers 0-3 */ + uint32_t vidtcon[4]; /* Video time control registers 0-3 */ + uint32_t shadowcon; /* Window shadow control register */ + uint32_t winchmap; /* Channel mapping control register */ + uint32_t vidintcon[2]; /* Video interrupt control registers */ + uint32_t dithmode; /* Dithering control register */ + uint32_t wpalcon[2]; /* Window palette control registers */ + uint32_t trigcon; /* Trigger control register */ + uint32_t i80ifcon[4]; /* I80 interface control registers */ + uint32_t colorgaincon; /* Color gain control register */ + uint32_t ldi_cmdcon[2]; /* LCD I80 interface command control */ + uint32_t sifccon[3]; /* I80 System Interface Manual Command Control */ + uint32_t huecoef_cr[4]; /* Hue control registers */ + uint32_t huecoef_cb[4]; /* Hue control registers */ + uint32_t hueoffset; /* Hue offset control register */ + uint32_t blendcon; /* Blending control register */ + uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */ + + Exynos4210fimdWindow window[5]; /* Window-specific registers */ + uint8_t *ifb; /* Internal frame buffer */ + bool invalidate; /* Image needs to be redrawn */ + bool enabled; /* Display controller is enabled */ +} Exynos4210fimdState; + +/* Perform byte/halfword/word swap of data according to WINCON */ +static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data) +{ + int i; + uint64_t res; + uint64_t x = *data; + + if (swap_ctl & FIMD_WINCON_SWAP_BITS) { + res = 0; + for (i = 0; i < 64; i++) { + if (x & (1ULL << (64 - i))) { + res |= (1ULL << i); + } + } + x = res; + } + + if (swap_ctl & FIMD_WINCON_SWAP_BYTE) { + x = bswap64(x); + } + + if (swap_ctl & FIMD_WINCON_SWAP_HWORD) { + x = ((x & 0x000000000000FFFFULL) << 48) | + ((x & 0x00000000FFFF0000ULL) << 16) | + ((x & 0x0000FFFF00000000ULL) >> 16) | + ((x & 0xFFFF000000000000ULL) >> 48); + } + + if (swap_ctl & FIMD_WINCON_SWAP_WORD) { + x = ((x & 0x00000000FFFFFFFFULL) << 32) | + ((x & 0xFFFFFFFF00000000ULL) >> 32); + } + + *data = x; +} + +/* Conversion routines of Pixel data from frame buffer area to internal RGBA + * pixel representation. + * Every color component internally represented as 8-bit value. If original + * data has less than 8 bit for component, data is extended to 8 bit. For + * example, if blue component has only two possible values 0 and 1 it will be + * extended to 0 and 0xFF */ + +/* One bit for alpha representation */ +#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + pixel >>= (R); \ + p->a = (pixel & 0x1); \ +} + +DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4) +DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5) +DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6) +DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5) +DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8) +DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7) + +/* Alpha component is always zero */ +#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + p->a = 0x0; \ +} + +DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb, 5, 6, 5) +DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb, 5, 5, 5) +DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb, 6, 6, 6) +DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb, 8, 8, 8) + +/* Alpha component has some meaningful value */ +#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + pixel >>= (R); \ + p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \ + ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \ + p->a = p->a | (p->a << 8) | (p->a << 16); \ +} + +DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4) +DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8) + +/* Lookup table to extent 2-bit color component to 8 bit */ +static const uint8_t pixel_lutable_2b[4] = { + 0x0, 0x55, 0xAA, 0xFF +}; +/* Lookup table to extent 3-bit color component to 8 bit */ +static const uint8_t pixel_lutable_3b[8] = { + 0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF +}; +/* Special case for a232 bpp mode */ +static void pixel_a232_to_rgb(uint32_t pixel, rgba *p) +{ + p->b = pixel_lutable_2b[(pixel & 0x3)]; + pixel >>= 2; + p->g = pixel_lutable_3b[(pixel & 0x7)]; + pixel >>= 3; + p->r = pixel_lutable_2b[(pixel & 0x3)]; + pixel >>= 2; + p->a = (pixel & 0x1); +} + +/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB + * for all three color components */ +static void pixel_1555_to_rgb(uint32_t pixel, rgba *p) +{ + uint8_t comm = (pixel >> 15) & 1; + p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + pixel >>= 5; + p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + pixel >>= 5; + p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + p->a = 0x0; +} + +/* Put/get pixel to/from internal LCD Controller framebuffer */ + +static int put_pixel_ifb(const rgba p, uint8_t *d) +{ + *(uint8_t *)d++ = p.r; + *(uint8_t *)d++ = p.g; + *(uint8_t *)d++ = p.b; + *(uint32_t *)d = p.a; + return RGBA_SIZE; +} + +static int get_pixel_ifb(const uint8_t *s, rgba *p) +{ + p->r = *(uint8_t *)s++; + p->g = *(uint8_t *)s++; + p->b = *(uint8_t *)s++; + p->a = (*(uint32_t *)s) & 0x00FFFFFF; + return RGBA_SIZE; +} + +static pixel_to_rgb_func *palette_data_format[8] = { + [0] = pixel_565_to_rgb, + [1] = pixel_a555_to_rgb, + [2] = pixel_666_to_rgb, + [3] = pixel_a665_to_rgb, + [4] = pixel_a666_to_rgb, + [5] = pixel_888_to_rgb, + [6] = pixel_a888_to_rgb, + [7] = pixel_8888_to_rgb +}; + +/* Returns Index in palette data formats table for given window number WINDOW */ +static uint32_t +exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window) +{ + uint32_t ret; + + switch (window) { + case 0: + ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L; + if (ret != 7) { + ret = 6 - ret; + } + break; + case 1: + ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L; + if (ret != 7) { + ret = 6 - ret; + } + break; + case 2: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L); + break; + case 3: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L); + break; + case 4: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L); + break; + default: + hw_error("exynos4210.fimd: incorrect window number %d\n", window); + ret = 0; + break; + } + return ret; +} + +#define FIMD_1_MINUS_COLOR(x) \ + ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \ + (0xFF0000 - ((x) & 0xFF0000))) +#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0)) +#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F)) + +/* Multiply three lower bytes of two 32-bit words with each other. + * Each byte with values 0-255 is considered as a number with possible values + * in a range [0 - 1] */ +static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b) +{ + uint32_t tmp; + uint32_t ret; + + ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp; + ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF00 : tmp << 8; + ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF0000 : tmp << 16; + return ret; +} + +/* For each corresponding bytes of two 32-bit words: (a*b + c*d) + * Byte values 0-255 are mapped to a range [0 .. 1] */ +static inline uint32_t +fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d) +{ + uint32_t tmp; + uint32_t ret; + + ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF)) + > 0xFF) ? 0xFF : tmp; + ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) * + ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8; + ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) + + ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF0000 : tmp << 16; + return ret; +} + +/* These routines cover all possible sources of window's transparent factor + * used in blending equation. Choice of routine is affected by WPALCON + * registers, BLENDCON register and window's WINCON register */ + +static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return pix_a; +} + +static uint32_t +fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_LOWER_HALFBYTE(pix_a); +} + +static uint32_t +fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(pix_a); +} + +static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return fimd_mult_each_byte(pix_a, w->alpha_val[0]); +} + +static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a), + EXTEND_UPPER_HALFBYTE(w->alpha_val[0])); +} + +static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return w->alpha_val[pix_a]; +} + +static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]); +} + +static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0]; +} + +static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon & + FIMD_WINCON_ALPHA_SEL) ? 1 : 0]); +} + +/* Updates currently active alpha value get function for specified window */ +static void fimd_update_get_alpha(Exynos4210fimdState *s, int win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT; + + if (w->wincon & FIMD_WINCON_BLD_PIX) { + if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) { + /* In this case, alpha component contains meaningful value */ + if (w->wincon & FIMD_WINCON_ALPHA_MUL) { + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_mult : fimd_get_alpha_mult_ext; + } else { + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_pix : fimd_get_alpha_pix_extlow; + } + } else { + if (IS_PALETTIZED_MODE(w) && + PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) { + /* Alpha component has 8-bit numeric value */ + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh; + } else { + /* Alpha has only two possible values (AEN) */ + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_aen : fimd_get_alpha_aen_ext; + } + } + } else { + w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel : + fimd_get_alpha_sel_ext; + } +} + +/* Blends current window's (w) pixel (foreground pixel *ret) with background + * window (w_blend) pixel p_bg according to formula: + * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR + * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA + */ +static void +exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) +{ + rgba p_fg = *ret; + uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) | + (p_bg.b & 0xFF); + uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) | + (p_fg.b & 0xFF); + uint32_t alpha_fg = p_fg.a; + int i; + /* It is possible that blending equation parameters a and b do not + * depend on window BLENEQ register. Account for this with first_coef */ + enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4}; + uint32_t first_coef = A_COEF; + uint32_t blend_param[COEF_NUM]; + + if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) { + uint32_t colorkey = (w->keycon[1] & + ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY; + + if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) && + (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { + /* Foreground pixel is displayed */ + if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { + alpha_fg = w->keyalpha; + blend_param[A_COEF] = alpha_fg; + blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); + } else { + alpha_fg = 0; + blend_param[A_COEF] = 0xFFFFFF; + blend_param[B_COEF] = 0x0; + } + first_coef = P_COEF; + } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 && + (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { + /* Background pixel is displayed */ + if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { + alpha_fg = w->keyalpha; + blend_param[A_COEF] = alpha_fg; + blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); + } else { + alpha_fg = 0; + blend_param[A_COEF] = 0x0; + blend_param[B_COEF] = 0xFFFFFF; + } + first_coef = P_COEF; + } + } + + for (i = first_coef; i < COEF_NUM; i++) { + switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) { + case 0: + blend_param[i] = 0; + break; + case 1: + blend_param[i] = 0xFFFFFF; + break; + case 2: + blend_param[i] = alpha_fg; + break; + case 3: + blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg); + break; + case 4: + blend_param[i] = p_bg.a; + break; + case 5: + blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a); + break; + case 6: + blend_param[i] = w->alpha_val[0]; + break; + case 10: + blend_param[i] = fg_color; + break; + case 11: + blend_param[i] = FIMD_1_MINUS_COLOR(fg_color); + break; + case 12: + blend_param[i] = bg_color; + break; + case 13: + blend_param[i] = FIMD_1_MINUS_COLOR(bg_color); + break; + default: + hw_error("exynos4210.fimd: blend equation coef illegal value\n"); + break; + } + } + + fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF], + fg_color, blend_param[A_COEF]); + ret->b = fg_color & 0xFF; + fg_color >>= 8; + ret->g = fg_color & 0xFF; + fg_color >>= 8; + ret->r = fg_color & 0xFF; + ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF], + p_bg.a, blend_param[Q_COEF]); +} + +/* These routines read data from video frame buffer in system RAM, convert + * this data to display controller internal representation, if necessary, + * perform pixel blending with data, currently presented in internal buffer. + * Result is stored in display controller internal frame buffer. */ + +/* Draw line with index in palette table in RAM frame buffer data */ +#define DEF_DRAW_LINE_PALETTE(N) \ +static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ + uint8_t *dst, bool blend) \ +{ \ + int width = w->rightbot_x - w->lefttop_x + 1; \ + uint8_t *ifb = dst; \ + uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ + uint64_t data; \ + rgba p, p_old; \ + int i; \ + do { \ + data = ldq_raw((void *)src); \ + src += 8; \ + fimd_swap_data(swap, &data); \ + for (i = (64 / (N) - 1); i >= 0; i--) { \ + w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \ + ((1ULL << (N)) - 1)], &p); \ + p.a = w->get_alpha(w, p.a); \ + if (blend) { \ + ifb += get_pixel_ifb(ifb, &p_old); \ + exynos4210_fimd_blend_pixel(w, p_old, &p); \ + } \ + dst += put_pixel_ifb(p, dst); \ + } \ + width -= (64 / (N)); \ + } while (width > 0); \ +} + +/* Draw line with direct color value in RAM frame buffer data */ +#define DEF_DRAW_LINE_NOPALETTE(N) \ +static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ + uint8_t *dst, bool blend) \ +{ \ + int width = w->rightbot_x - w->lefttop_x + 1; \ + uint8_t *ifb = dst; \ + uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ + uint64_t data; \ + rgba p, p_old; \ + int i; \ + do { \ + data = ldq_raw((void *)src); \ + src += 8; \ + fimd_swap_data(swap, &data); \ + for (i = (64 / (N) - 1); i >= 0; i--) { \ + w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \ + p.a = w->get_alpha(w, p.a); \ + if (blend) { \ + ifb += get_pixel_ifb(ifb, &p_old); \ + exynos4210_fimd_blend_pixel(w, p_old, &p); \ + } \ + dst += put_pixel_ifb(p, dst); \ + } \ + width -= (64 / (N)); \ + } while (width > 0); \ +} + +DEF_DRAW_LINE_PALETTE(1) +DEF_DRAW_LINE_PALETTE(2) +DEF_DRAW_LINE_PALETTE(4) +DEF_DRAW_LINE_PALETTE(8) +DEF_DRAW_LINE_NOPALETTE(8) /* 8bpp mode has palette and non-palette versions */ +DEF_DRAW_LINE_NOPALETTE(16) +DEF_DRAW_LINE_NOPALETTE(32) + +/* Special draw line routine for window color map case */ +static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, + uint8_t *dst, bool blend) +{ + rgba p, p_old; + uint8_t *ifb = dst; + int width = w->rightbot_x - w->lefttop_x + 1; + uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; + + do { + pixel_888_to_rgb(map_color, &p); + p.a = w->get_alpha(w, p.a); + if (blend) { + ifb += get_pixel_ifb(ifb, &p_old); + exynos4210_fimd_blend_pixel(w, p_old, &p); + } + dst += put_pixel_ifb(p, dst); + } while (--width); +} + +/* Write RGB to QEMU's GraphicConsole framebuffer */ + +static int put_to_qemufb_pixel8(const rgba p, uint8_t *d) +{ + uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b); + *(uint8_t *)d = pixel; + return 1; +} + +static int put_to_qemufb_pixel15(const rgba p, uint8_t *d) +{ + uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b); + *(uint16_t *)d = pixel; + return 2; +} + +static int put_to_qemufb_pixel16(const rgba p, uint8_t *d) +{ + uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b); + *(uint16_t *)d = pixel; + return 2; +} + +static int put_to_qemufb_pixel24(const rgba p, uint8_t *d) +{ + uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b); + *(uint8_t *)d++ = (pixel >> 0) & 0xFF; + *(uint8_t *)d++ = (pixel >> 8) & 0xFF; + *(uint8_t *)d++ = (pixel >> 16) & 0xFF; + return 3; +} + +static int put_to_qemufb_pixel32(const rgba p, uint8_t *d) +{ + uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b); + *(uint32_t *)d = pixel; + return 4; +} + +/* Routine to copy pixel from internal buffer to QEMU buffer */ +static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel); +static inline void fimd_update_putpix_qemu(int bpp) +{ + switch (bpp) { + case 8: + put_pixel_toqemu = put_to_qemufb_pixel8; + break; + case 15: + put_pixel_toqemu = put_to_qemufb_pixel15; + break; + case 16: + put_pixel_toqemu = put_to_qemufb_pixel16; + break; + case 24: + put_pixel_toqemu = put_to_qemufb_pixel24; + break; + case 32: + put_pixel_toqemu = put_to_qemufb_pixel32; + break; + default: + hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp); + break; + } +} + +/* Routine to copy a line from internal frame buffer to QEMU display */ +static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst) +{ + rgba p; + + do { + src += get_pixel_ifb(src, &p); + dst += put_pixel_toqemu(p, dst); + } while (--width); +} + +/* Parse BPPMODE_F = WINCON1[5:2] bits */ +static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + + if (w->winmap & FIMD_WINMAP_EN) { + w->draw_line = draw_line_mapcolor; + return; + } + + switch (WIN_BPP_MODE(w)) { + case 0: + w->draw_line = draw_line_palette_1; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 1: + w->draw_line = draw_line_palette_2; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 2: + w->draw_line = draw_line_palette_4; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 3: + w->draw_line = draw_line_palette_8; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 4: + w->draw_line = draw_line_8; + w->pixel_to_rgb = pixel_a232_to_rgb; + break; + case 5: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_565_to_rgb; + break; + case 6: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_a555_to_rgb; + break; + case 7: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_1555_to_rgb; + break; + case 8: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_666_to_rgb; + break; + case 9: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a665_to_rgb; + break; + case 10: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a666_to_rgb; + break; + case 11: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_888_to_rgb; + break; + case 12: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a887_to_rgb; + break; + case 13: + w->draw_line = draw_line_32; + if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & + FIMD_WINCON_ALPHA_SEL)) { + w->pixel_to_rgb = pixel_8888_to_rgb; + } else { + w->pixel_to_rgb = pixel_a888_to_rgb; + } + break; + case 14: + w->draw_line = draw_line_16; + if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & + FIMD_WINCON_ALPHA_SEL)) { + w->pixel_to_rgb = pixel_4444_to_rgb; + } else { + w->pixel_to_rgb = pixel_a444_to_rgb; + } + break; + case 15: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_555_to_rgb; + break; + } +} + +#if EXYNOS4210_FIMD_MODE_TRACE > 0 +static const char *exynos4210_fimd_get_bppmode(int mode_code) +{ + switch (mode_code) { + case 0: + return "1 bpp"; + case 1: + return "2 bpp"; + case 2: + return "4 bpp"; + case 3: + return "8 bpp (palettized)"; + case 4: + return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)"; + case 5: + return "16 bpp (non-palettized, R:5-G:6-B:5)"; + case 6: + return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)"; + case 7: + return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)"; + case 8: + return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)"; + case 9: + return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)"; + case 10: + return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)"; + case 11: + return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)"; + case 12: + return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)"; + case 13: + return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)"; + case 14: + return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)"; + case 15: + return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)"; + default: + return "Non-existing bpp mode"; + } +} + +static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, + int win_num, uint32_t val) +{ + Exynos4210fimdWindow *w = &s->window[win_num]; + + if (w->winmap & FIMD_WINMAP_EN) { + printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n", + win_num, w->winmap & 0xFFFFFF); + return; + } + + if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) { + return; + } + printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num, + exynos4210_fimd_get_bppmode((val >> 2) & 0xF)); +} +#else +static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, + int win_num, uint32_t val) +{ + +} +#endif + +static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w) +{ + switch (w->wincon & FIMD_WINCON_BUFSTATUS) { + case FIMD_WINCON_BUF0_STAT: + return 0; + case FIMD_WINCON_BUF1_STAT: + return 1; + case FIMD_WINCON_BUF2_STAT: + return 2; + default: + DPRINT_ERROR("Non-existent buffer index\n"); + return 0; + } +} + +/* Updates specified window's MemorySection based on values of WINCON, + * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ +static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + target_phys_addr_t fb_start_addr, fb_mapped_len; + + if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) || + FIMD_WINDOW_PROTECTED(s->shadowcon, win)) { + return; + } + + if (w->host_fb_addr) { + cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0); + w->host_fb_addr = NULL; + w->fb_len = 0; + } + + fb_start_addr = w->buf_start[fimd_get_buffer_id(w)]; + /* Total number of bytes of virtual screen used by current window */ + w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) * + (w->rightbot_y - w->lefttop_y + 1); + w->mem_section = memory_region_find(sysbus_address_space(&s->busdev), + fb_start_addr, w->fb_len); + assert(w->mem_section.mr); + assert(w->mem_section.offset_within_address_space == fb_start_addr); + DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n", + win, fb_start_addr, w->fb_len); + + if (w->mem_section.size != w->fb_len || + !memory_region_is_ram(w->mem_section.mr)) { + DPRINT_ERROR("Failed to find window %u framebuffer region\n", win); + goto error_return; + } + + w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0); + if (!w->host_fb_addr) { + DPRINT_ERROR("Failed to map window %u framebuffer\n", win); + goto error_return; + } + + if (fb_mapped_len != w->fb_len) { + DPRINT_ERROR("Window %u mapped framebuffer length is less then " + "expected\n", win); + cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0); + goto error_return; + } + return; + +error_return: + w->mem_section.mr = NULL; + w->mem_section.size = 0; + w->host_fb_addr = NULL; + w->fb_len = 0; +} + +static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled) +{ + if (enabled && !s->enabled) { + unsigned w; + s->enabled = true; + for (w = 0; w < NUM_OF_WINDOWS; w++) { + fimd_update_memory_section(s, w); + } + } + s->enabled = enabled; + DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled"); +} + +static inline uint32_t unpack_upper_4(uint32_t x) +{ + return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4); +} + +static inline uint32_t pack_upper_4(uint32_t x) +{ + return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) | + ((x & 0xF0) >> 4)) & 0xFFF; +} + +static void exynos4210_fimd_update_irq(Exynos4210fimdState *s) +{ + if (!(s->vidintcon[0] & FIMD_VIDINT_INTEN)) { + qemu_irq_lower(s->irq[0]); + qemu_irq_lower(s->irq[1]); + qemu_irq_lower(s->irq[2]); + return; + } + if ((s->vidintcon[0] & FIMD_VIDINT_INTFIFOEN) && + (s->vidintcon[1] & FIMD_VIDINT_INTFIFOPEND)) { + qemu_irq_raise(s->irq[0]); + } else { + qemu_irq_lower(s->irq[0]); + } + if ((s->vidintcon[0] & FIMD_VIDINT_INTFRMEN) && + (s->vidintcon[1] & FIMD_VIDINT_INTFRMPEND)) { + qemu_irq_raise(s->irq[1]); + } else { + qemu_irq_lower(s->irq[1]); + } + if ((s->vidintcon[0] & FIMD_VIDINT_I80IFDONE) && + (s->vidintcon[1] & FIMD_VIDINT_INTI80PEND)) { + qemu_irq_raise(s->irq[2]); + } else { + qemu_irq_lower(s->irq[2]); + } +} + +static void exynos4210_fimd_invalidate(void *opaque) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + s->invalidate = true; +} + +static void exynos4210_update_resolution(Exynos4210fimdState *s) +{ + /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ + uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; + uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; + + if (s->ifb == NULL || ds_get_width(s->console) != width || + ds_get_height(s->console) != height) { + DPRINT_L1("Resolution changed from %ux%u to %ux%u\n", + ds_get_width(s->console), ds_get_height(s->console), width, height); + qemu_console_resize(s->console, width, height); + s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1); + memset(s->ifb, 0, width * height * RGBA_SIZE + 1); + exynos4210_fimd_invalidate(s); + } +} + +static void exynos4210_fimd_update(void *opaque) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + Exynos4210fimdWindow *w; + int i, line; + target_phys_addr_t fb_line_addr, inc_size; + int scrn_height; + int first_line = -1, last_line = -1, scrn_width; + bool blend = false; + uint8_t *host_fb_addr; + bool is_dirty = false; + const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; + const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; + + if (!s || !s->console || !ds_get_bits_per_pixel(s->console) || + !s->enabled) { + return; + } + exynos4210_update_resolution(s); + + for (i = 0; i < NUM_OF_WINDOWS; i++) { + w = &s->window[i]; + if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { + scrn_height = w->rightbot_y - w->lefttop_y + 1; + scrn_width = w->virtpage_width; + /* Total width of virtual screen page in bytes */ + inc_size = scrn_width + w->virtpage_offsize; + memory_region_sync_dirty_bitmap(w->mem_section.mr); + host_fb_addr = w->host_fb_addr; + fb_line_addr = w->mem_section.offset_within_region; + + for (line = 0; line < scrn_height; line++) { + is_dirty = memory_region_get_dirty(w->mem_section.mr, + fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); + + if (s->invalidate || is_dirty) { + if (first_line == -1) { + first_line = line; + } + last_line = line; + w->draw_line(w, host_fb_addr, s->ifb + + w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * + global_width * RGBA_SIZE, blend); + } + host_fb_addr += inc_size; + fb_line_addr += inc_size; + is_dirty = false; + } + memory_region_reset_dirty(w->mem_section.mr, + w->mem_section.offset_within_region, + w->fb_len, DIRTY_MEMORY_VGA); + blend = true; + } + } + + /* Copy resulting image to QEMU_CONSOLE. */ + if (first_line >= 0) { + uint8_t *d; + int bpp; + + bpp = ds_get_bits_per_pixel(s->console); + fimd_update_putpix_qemu(bpp); + bpp = (bpp + 1) >> 3; + d = ds_get_data(s->console); + for (line = first_line; line <= last_line; line++) { + fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * + RGBA_SIZE, d + global_width * line * bpp); + } + dpy_update(s->console, 0, 0, global_width, global_height); + } + s->invalidate = false; + s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND; + if ((s->vidcon[0] & FIMD_VIDCON0_ENVID_F) == 0) { + exynos4210_fimd_enable(s, false); + } + exynos4210_fimd_update_irq(s); +} + +static void exynos4210_fimd_reset(DeviceState *d) +{ + Exynos4210fimdState *s = DO_UPCAST(Exynos4210fimdState, busdev.qdev, d); + unsigned w; + + DPRINT_TRACE("Display controller reset\n"); + /* Set all display controller registers to 0 */ + memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon); + for (w = 0; w < NUM_OF_WINDOWS; w++) { + memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow)); + s->window[w].blendeq = 0xC2; + exynos4210_fimd_update_win_bppmode(s, w); + exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); + fimd_update_get_alpha(s, w); + } + + if (s->ifb != NULL) { + g_free(s->ifb); + } + s->ifb = NULL; + + exynos4210_fimd_invalidate(s); + exynos4210_fimd_enable(s, false); + /* Some registers have non-zero initial values */ + s->winchmap = 0x7D517D51; + s->colorgaincon = 0x10040100; + s->huecoef_cr[0] = s->huecoef_cr[3] = 0x01000100; + s->huecoef_cb[0] = s->huecoef_cb[3] = 0x01000100; + s->hueoffset = 0x01800080; +} + +static void exynos4210_fimd_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + unsigned w, i; + uint32_t old_value; + + DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset, + (long long unsigned int)val, (long long unsigned int)val); + + switch (offset) { + case FIMD_VIDCON0: + if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) { + exynos4210_fimd_enable(s, true); + } else { + if ((val & FIMD_VIDCON0_ENVID) == 0) { + exynos4210_fimd_enable(s, false); + } + } + s->vidcon[0] = val; + break; + case FIMD_VIDCON1: + /* Leave read-only bits as is */ + val = (val & (~FIMD_VIDCON1_ROMASK)) | + (s->vidcon[1] & FIMD_VIDCON1_ROMASK); + s->vidcon[1] = val; + break; + case FIMD_VIDCON2 ... FIMD_VIDCON3: + s->vidcon[(offset) >> 2] = val; + break; + case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: + s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val; + break; + case FIMD_WINCON_START ... FIMD_WINCON_END: + w = (offset - FIMD_WINCON_START) >> 2; + /* Window's current buffer ID */ + i = fimd_get_buffer_id(&s->window[w]); + old_value = s->window[w].wincon; + val = (val & ~FIMD_WINCON_ROMASK) | + (s->window[w].wincon & FIMD_WINCON_ROMASK); + if (w == 0) { + /* Window 0 wincon ALPHA_MUL bit must always be 0 */ + val &= ~FIMD_WINCON_ALPHA_MUL; + } + exynos4210_fimd_trace_bppmode(s, w, val); + switch (val & FIMD_WINCON_BUFSELECT) { + case FIMD_WINCON_BUF0_SEL: + val &= ~FIMD_WINCON_BUFSTATUS; + break; + case FIMD_WINCON_BUF1_SEL: + val = (val & ~FIMD_WINCON_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L; + break; + case FIMD_WINCON_BUF2_SEL: + if (val & FIMD_WINCON_BUFMODE) { + val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H; + } + break; + default: + break; + } + s->window[w].wincon = val; + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + if ((i != fimd_get_buffer_id(&s->window[w])) || + (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon & + FIMD_WINCON_ENWIN))) { + fimd_update_memory_section(s, w); + } + break; + case FIMD_SHADOWCON: + old_value = s->shadowcon; + s->shadowcon = val; + for (w = 0; w < NUM_OF_WINDOWS; w++) { + if (FIMD_WINDOW_PROTECTED(old_value, w) && + !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) { + fimd_update_memory_section(s, w); + } + } + break; + case FIMD_WINCHMAP: + s->winchmap = val; + break; + case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: + w = (offset - FIMD_VIDOSD_START) >> 4; + i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; + switch (i) { + case 0: + old_value = s->window[w].lefttop_y; + s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + if (s->window[w].lefttop_y != old_value) { + fimd_update_memory_section(s, w); + } + break; + case 1: + old_value = s->window[w].rightbot_y; + s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + if (s->window[w].rightbot_y != old_value) { + fimd_update_memory_section(s, w); + } + break; + case 2: + if (w == 0) { + s->window[w].osdsize = val; + } else { + s->window[w].alpha_val[0] = + unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >> + FIMD_VIDOSD_AEN0_SHIFT) | + (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER); + s->window[w].alpha_val[1] = + unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) | + (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER); + } + break; + case 3: + if (w != 1 && w != 2) { + DPRINT_ERROR("Bad write offset 0x%08x\n", offset); + return; + } + s->window[w].osdsize = val; + break; + } + break; + case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: + w = (offset - FIMD_VIDWADD0_START) >> 3; + i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; + if (i == fimd_get_buffer_id(&s->window[w]) && + s->window[w].buf_start[i] != val) { + s->window[w].buf_start[i] = val; + fimd_update_memory_section(s, w); + break; + } + s->window[w].buf_start[i] = val; + break; + case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: + w = (offset - FIMD_VIDWADD1_START) >> 3; + i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; + s->window[w].buf_end[i] = val; + break; + case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: + w = (offset - FIMD_VIDWADD2_START) >> 2; + if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) || + (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) != + s->window[w].virtpage_offsize)) { + s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH; + s->window[w].virtpage_offsize = + (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE; + fimd_update_memory_section(s, w); + } + break; + case FIMD_VIDINTCON0: + s->vidintcon[0] = val; + break; + case FIMD_VIDINTCON1: + s->vidintcon[1] &= ~(val & 7); + exynos4210_fimd_update_irq(s); + break; + case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: + w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; + i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; + s->window[w].keycon[i] = val; + break; + case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: + w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; + s->window[w].keyalpha = val; + break; + case FIMD_DITHMODE: + s->dithmode = val; + break; + case FIMD_WINMAP_START ... FIMD_WINMAP_END: + w = (offset - FIMD_WINMAP_START) >> 2; + old_value = s->window[w].winmap; + s->window[w].winmap = val; + if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) { + exynos4210_fimd_invalidate(s); + exynos4210_fimd_update_win_bppmode(s, w); + exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); + exynos4210_fimd_update(s); + } + break; + case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: + i = (offset - FIMD_WPALCON_HIGH) >> 2; + s->wpalcon[i] = val; + if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) { + for (w = 0; w < NUM_OF_WINDOWS; w++) { + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + } + } + break; + case FIMD_TRIGCON: + val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK); + s->trigcon = val; + break; + case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: + s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val; + break; + case FIMD_COLORGAINCON: + s->colorgaincon = val; + break; + case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: + s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val; + break; + case FIMD_SIFCCON0 ... FIMD_SIFCCON2: + i = (offset - FIMD_SIFCCON0) >> 2; + if (i != 2) { + s->sifccon[i] = val; + } + break; + case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: + i = (offset - FIMD_HUECOEFCR_START) >> 2; + s->huecoef_cr[i] = val; + break; + case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: + i = (offset - FIMD_HUECOEFCB_START) >> 2; + s->huecoef_cb[i] = val; + break; + case FIMD_HUEOFFSET: + s->hueoffset = val; + break; + case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: + w = ((offset - FIMD_VIDWALPHA_START) >> 3); + i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; + if (w == 0) { + s->window[w].alpha_val[i] = val; + } else { + s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) | + (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER); + } + break; + case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: + s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val; + break; + case FIMD_BLENDCON: + old_value = s->blendcon; + s->blendcon = val; + if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) { + for (w = 0; w < NUM_OF_WINDOWS; w++) { + fimd_update_get_alpha(s, w); + } + } + break; + case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: + s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val; + break; + case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: + s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val; + break; + case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + w = (offset - FIMD_VIDW0ADD0_B2) >> 3; + if (fimd_get_buffer_id(&s->window[w]) == 2 && + s->window[w].buf_start[2] != val) { + s->window[w].buf_start[2] = val; + fimd_update_memory_section(s, w); + break; + } + s->window[w].buf_start[2] = val; + break; + case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val; + break; + case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val; + break; + case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: + s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val; + break; + case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: + w = (offset - FIMD_PAL_MEM_START) >> 10; + i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; + s->window[w].palette[i] = val; + break; + case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: + /* Palette memory aliases for windows 0 and 1 */ + w = (offset - FIMD_PALMEM_AL_START) >> 10; + i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; + s->window[w].palette[i] = val; + break; + default: + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } +} + +static uint64_t exynos4210_fimd_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + int w, i; + uint32_t ret = 0; + + DPRINT_L2("read offset 0x%08x\n", offset); + + switch (offset) { + case FIMD_VIDCON0 ... FIMD_VIDCON3: + return s->vidcon[(offset - FIMD_VIDCON0) >> 2]; + case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: + return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2]; + case FIMD_WINCON_START ... FIMD_WINCON_END: + return s->window[(offset - FIMD_WINCON_START) >> 2].wincon; + case FIMD_SHADOWCON: + return s->shadowcon; + case FIMD_WINCHMAP: + return s->winchmap; + case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: + w = (offset - FIMD_VIDOSD_START) >> 4; + i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; + switch (i) { + case 0: + ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) << + FIMD_VIDOSD_HOR_SHIFT) | + (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK); + break; + case 1: + ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) << + FIMD_VIDOSD_HOR_SHIFT) | + (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK); + break; + case 2: + if (w == 0) { + ret = s->window[w].osdsize; + } else { + ret = (pack_upper_4(s->window[w].alpha_val[0]) << + FIMD_VIDOSD_AEN0_SHIFT) | + pack_upper_4(s->window[w].alpha_val[1]); + } + break; + case 3: + if (w != 1 && w != 2) { + DPRINT_ERROR("bad read offset 0x%08x\n", offset); + return 0xBAADBAAD; + } + ret = s->window[w].osdsize; + break; + } + return ret; + case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: + w = (offset - FIMD_VIDWADD0_START) >> 3; + i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; + return s->window[w].buf_start[i]; + case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: + w = (offset - FIMD_VIDWADD1_START) >> 3; + i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; + return s->window[w].buf_end[i]; + case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: + w = (offset - FIMD_VIDWADD2_START) >> 2; + return s->window[w].virtpage_width | (s->window[w].virtpage_offsize << + FIMD_VIDWADD2_OFFSIZE_SHIFT); + case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1: + return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2]; + case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: + w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; + i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; + return s->window[w].keycon[i]; + case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: + w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; + return s->window[w].keyalpha; + case FIMD_DITHMODE: + return s->dithmode; + case FIMD_WINMAP_START ... FIMD_WINMAP_END: + return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap; + case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: + return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2]; + case FIMD_TRIGCON: + return s->trigcon; + case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: + return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2]; + case FIMD_COLORGAINCON: + return s->colorgaincon; + case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: + return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2]; + case FIMD_SIFCCON0 ... FIMD_SIFCCON2: + i = (offset - FIMD_SIFCCON0) >> 2; + return s->sifccon[i]; + case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: + i = (offset - FIMD_HUECOEFCR_START) >> 2; + return s->huecoef_cr[i]; + case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: + i = (offset - FIMD_HUECOEFCB_START) >> 2; + return s->huecoef_cb[i]; + case FIMD_HUEOFFSET: + return s->hueoffset; + case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: + w = ((offset - FIMD_VIDWALPHA_START) >> 3); + i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; + return s->window[w].alpha_val[i] & + (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER); + case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: + return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq; + case FIMD_BLENDCON: + return s->blendcon; + case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: + return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon; + case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: + return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2]; + case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2]; + case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start; + case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end; + case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: + return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size; + case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: + w = (offset - FIMD_PAL_MEM_START) >> 10; + i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; + return s->window[w].palette[i]; + case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: + /* Palette aliases for win 0,1 */ + w = (offset - FIMD_PALMEM_AL_START) >> 10; + i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; + return s->window[w].palette[i]; + } + + DPRINT_ERROR("bad read offset 0x%08x\n", offset); + return 0xBAADBAAD; +} + +static const MemoryRegionOps exynos4210_fimd_mmio_ops = { + .read = exynos4210_fimd_read, + .write = exynos4210_fimd_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int exynos4210_fimd_load(void *opaque, int version_id) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + int w; + + if (version_id != 1) { + return -EINVAL; + } + + for (w = 0; w < NUM_OF_WINDOWS; w++) { + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + fimd_update_memory_section(s, w); + } + + /* Redraw the whole screen */ + exynos4210_update_resolution(s); + exynos4210_fimd_invalidate(s); + exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) == + FIMD_VIDCON0_ENVID_MASK); + return 0; +} + +static const VMStateDescription exynos4210_fimd_window_vmstate = { + .name = "exynos4210.fimd_window", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(wincon, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3), + VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3), + VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2), + VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow), + VMSTATE_UINT32(winmap, Exynos4210fimdWindow), + VMSTATE_UINT32(blendeq, Exynos4210fimdWindow), + VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256), + VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow), + VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow), + VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow), + VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow), + VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow), + VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow), + VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow), + VMSTATE_UINT32(osdsize, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2), + VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow), + VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription exynos4210_fimd_vmstate = { + .name = "exynos4210.fimd", + .version_id = 1, + .minimum_version_id = 1, + .post_load = exynos4210_fimd_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4), + VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4), + VMSTATE_UINT32(shadowcon, Exynos4210fimdState), + VMSTATE_UINT32(winchmap, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2), + VMSTATE_UINT32(dithmode, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2), + VMSTATE_UINT32(trigcon, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4), + VMSTATE_UINT32(colorgaincon, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2), + VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3), + VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4), + VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4), + VMSTATE_UINT32(hueoffset, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12), + VMSTATE_UINT32(blendcon, Exynos4210fimdState), + VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1, + exynos4210_fimd_window_vmstate, Exynos4210fimdWindow), + VMSTATE_END_OF_LIST() + } +}; + +static int exynos4210_fimd_init(SysBusDevice *dev) +{ + Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, dev); + + s->ifb = NULL; + + sysbus_init_irq(dev, &s->irq[0]); + sysbus_init_irq(dev, &s->irq[1]); + sysbus_init_irq(dev, &s->irq[2]); + + memory_region_init_io(&s->iomem, &exynos4210_fimd_mmio_ops, s, + "exynos4210.fimd", FIMD_REGS_SIZE); + sysbus_init_mmio(dev, &s->iomem); + s->console = graphic_console_init(exynos4210_fimd_update, + exynos4210_fimd_invalidate, NULL, NULL, s); + + return 0; +} + +static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + dc->vmsd = &exynos4210_fimd_vmstate; + dc->reset = exynos4210_fimd_reset; + k->init = exynos4210_fimd_init; +} + +static TypeInfo exynos4210_fimd_info = { + .name = "exynos4210.fimd", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210fimdState), + .class_init = exynos4210_fimd_class_init, +}; + +static void exynos4210_fimd_register_types(void) +{ + type_register_static(&exynos4210_fimd_info); +} + +type_init(exynos4210_fimd_register_types) diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c new file mode 100644 index 0000000000..ec13140f9f --- /dev/null +++ b/hw/exynos4210_gic.c @@ -0,0 +1,458 @@ +/* + * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin <e.voevodin@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-common.h" +#include "irq.h" +#include "exynos4210.h" + +enum ExtGicId { + EXT_GIC_ID_MDMA_LCD0 = 66, + EXT_GIC_ID_PDMA0, + EXT_GIC_ID_PDMA1, + EXT_GIC_ID_TIMER0, + EXT_GIC_ID_TIMER1, + EXT_GIC_ID_TIMER2, + EXT_GIC_ID_TIMER3, + EXT_GIC_ID_TIMER4, + EXT_GIC_ID_MCT_L0, + EXT_GIC_ID_WDT, + EXT_GIC_ID_RTC_ALARM, + EXT_GIC_ID_RTC_TIC, + EXT_GIC_ID_GPIO_XB, + EXT_GIC_ID_GPIO_XA, + EXT_GIC_ID_MCT_L1, + EXT_GIC_ID_IEM_APC, + EXT_GIC_ID_IEM_IEC, + EXT_GIC_ID_NFC, + EXT_GIC_ID_UART0, + EXT_GIC_ID_UART1, + EXT_GIC_ID_UART2, + EXT_GIC_ID_UART3, + EXT_GIC_ID_UART4, + EXT_GIC_ID_MCT_G0, + EXT_GIC_ID_I2C0, + EXT_GIC_ID_I2C1, + EXT_GIC_ID_I2C2, + EXT_GIC_ID_I2C3, + EXT_GIC_ID_I2C4, + EXT_GIC_ID_I2C5, + EXT_GIC_ID_I2C6, + EXT_GIC_ID_I2C7, + EXT_GIC_ID_SPI0, + EXT_GIC_ID_SPI1, + EXT_GIC_ID_SPI2, + EXT_GIC_ID_MCT_G1, + EXT_GIC_ID_USB_HOST, + EXT_GIC_ID_USB_DEVICE, + EXT_GIC_ID_MODEMIF, + EXT_GIC_ID_HSMMC0, + EXT_GIC_ID_HSMMC1, + EXT_GIC_ID_HSMMC2, + EXT_GIC_ID_HSMMC3, + EXT_GIC_ID_SDMMC, + EXT_GIC_ID_MIPI_CSI_4LANE, + EXT_GIC_ID_MIPI_DSI_4LANE, + EXT_GIC_ID_MIPI_CSI_2LANE, + EXT_GIC_ID_MIPI_DSI_2LANE, + EXT_GIC_ID_ONENAND_AUDI, + EXT_GIC_ID_ROTATOR, + EXT_GIC_ID_FIMC0, + EXT_GIC_ID_FIMC1, + EXT_GIC_ID_FIMC2, + EXT_GIC_ID_FIMC3, + EXT_GIC_ID_JPEG, + EXT_GIC_ID_2D, + EXT_GIC_ID_PCIe, + EXT_GIC_ID_MIXER, + EXT_GIC_ID_HDMI, + EXT_GIC_ID_HDMI_I2C, + EXT_GIC_ID_MFC, + EXT_GIC_ID_TVENC, +}; + +enum ExtInt { + EXT_GIC_ID_EXTINT0 = 48, + EXT_GIC_ID_EXTINT1, + EXT_GIC_ID_EXTINT2, + EXT_GIC_ID_EXTINT3, + EXT_GIC_ID_EXTINT4, + EXT_GIC_ID_EXTINT5, + EXT_GIC_ID_EXTINT6, + EXT_GIC_ID_EXTINT7, + EXT_GIC_ID_EXTINT8, + EXT_GIC_ID_EXTINT9, + EXT_GIC_ID_EXTINT10, + EXT_GIC_ID_EXTINT11, + EXT_GIC_ID_EXTINT12, + EXT_GIC_ID_EXTINT13, + EXT_GIC_ID_EXTINT14, + EXT_GIC_ID_EXTINT15 +}; + +/* + * External GIC sources which are not from External Interrupt Combiner or + * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ, + * which is INTG16 in Internal Interrupt Combiner. + */ + +static uint32_t +combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { + /* int combiner groups 16-19 */ + { }, { }, { }, { }, + /* int combiner group 20 */ + { 0, EXT_GIC_ID_MDMA_LCD0 }, + /* int combiner group 21 */ + { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 }, + /* int combiner group 22 */ + { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2, + EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 }, + /* int combiner group 23 */ + { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC }, + /* int combiner group 24 */ + { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA }, + /* int combiner group 25 */ + { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC }, + /* int combiner group 26 */ + { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3, + EXT_GIC_ID_UART4 }, + /* int combiner group 27 */ + { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3, + EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6, + EXT_GIC_ID_I2C7 }, + /* int combiner group 28 */ + { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 }, + /* int combiner group 29 */ + { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2, + EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC }, + /* int combiner group 30 */ + { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE }, + /* int combiner group 31 */ + { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE }, + /* int combiner group 32 */ + { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 }, + /* int combiner group 33 */ + { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 }, + /* int combiner group 34 */ + { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC }, + /* int combiner group 35 */ + { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, + /* int combiner group 36 */ + { EXT_GIC_ID_MIXER }, + /* int combiner group 37 */ + { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6, + EXT_GIC_ID_EXTINT7 }, + /* groups 38-50 */ + { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, + /* int combiner group 51 */ + { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, + /* group 52 */ + { }, + /* int combiner group 53 */ + { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, + /* groups 54-63 */ + { }, { }, { }, { }, { }, { }, { }, { }, { }, { } +}; + +#define EXYNOS4210_GIC_NIRQ 160 +#define NCPU EXYNOS4210_NCPUS + +#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000 +#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000 + +#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET 0x8000 +#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \ + ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET) +#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \ + ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET) + +#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100 +#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000 + +static void exynos4210_irq_handler(void *opaque, int irq, int level) +{ + Exynos4210Irq *s = (Exynos4210Irq *)opaque; + + /* Bypass */ + qemu_set_irq(s->board_irqs[irq], level); + + return; +} + +/* + * Initialize exynos4210 IRQ subsystem stub. + */ +qemu_irq *exynos4210_init_irq(Exynos4210Irq *s) +{ + return qemu_allocate_irqs(exynos4210_irq_handler, s, + EXYNOS4210_MAX_INT_COMBINER_IN_IRQ); +} + +/* + * Initialize board IRQs. + * These IRQs contain splitted Int/External Combiner and External Gic IRQs. + */ +void exynos4210_init_board_irqs(Exynos4210Irq *s) +{ + uint32_t grp, bit, irq_id, n; + + for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) { + s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], + s->ext_combiner_irq[n]); + + irq_id = 0; + if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) || + n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) { + /* MCT_G0 is passed to External GIC */ + irq_id = EXT_GIC_ID_MCT_G0; + } + if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) || + n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) { + /* MCT_G1 is passed to External and GIC */ + irq_id = EXT_GIC_ID_MCT_G1; + } + if (irq_id) { + s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], + s->ext_gic_irq[irq_id-32]); + } + + } + for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { + /* these IDs are passed to Internal Combiner and External GIC */ + grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n); + bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); + irq_id = combiner_grp_to_gic_id[grp - + EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit]; + + if (irq_id) { + s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], + s->ext_gic_irq[irq_id-32]); + } + } +} + +/* + * Get IRQ number from exynos4210 IRQ subsystem stub. + * To identify IRQ source use internal combiner group and bit number + * grp - group number + * bit - bit number inside group + */ +uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) +{ + return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit); +} + +/********* GIC part *********/ + +static inline int +gic_get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +#include "arm_gic.c" + +typedef struct { + gic_state gic; + MemoryRegion cpu_container; + MemoryRegion dist_container; + MemoryRegion cpu_alias[NCPU]; + MemoryRegion dist_alias[NCPU]; + uint32_t num_cpu; +} Exynos4210GicState; + +static int exynos4210_gic_init(SysBusDevice *dev) +{ + Exynos4210GicState *s = FROM_SYSBUSGIC(Exynos4210GicState, dev); + uint32_t i; + const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; + const char dist_prefix[] = "exynos4210-gic-alias_dist"; + char cpu_alias_name[sizeof(cpu_prefix) + 3]; + char dist_alias_name[sizeof(cpu_prefix) + 3]; + + gic_init(&s->gic, s->num_cpu, EXYNOS4210_GIC_NIRQ); + + memory_region_init(&s->cpu_container, "exynos4210-cpu-container", + EXYNOS4210_EXT_GIC_CPU_REGION_SIZE); + memory_region_init(&s->dist_container, "exynos4210-dist-container", + EXYNOS4210_EXT_GIC_DIST_REGION_SIZE); + + for (i = 0; i < s->num_cpu; i++) { + /* Map CPU interface per SMP Core */ + sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); + memory_region_init_alias(&s->cpu_alias[i], + cpu_alias_name, + &s->gic.cpuiomem[0], + 0, + EXYNOS4210_GIC_CPU_REGION_SIZE); + memory_region_add_subregion(&s->cpu_container, + EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]); + + /* Map Distributor per SMP Core */ + sprintf(dist_alias_name, "%s%x", dist_prefix, i); + memory_region_init_alias(&s->dist_alias[i], + dist_alias_name, + &s->gic.iomem, + 0, + EXYNOS4210_GIC_DIST_REGION_SIZE); + memory_region_add_subregion(&s->dist_container, + EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]); + } + + sysbus_init_mmio(dev, &s->cpu_container); + sysbus_init_mmio(dev, &s->dist_container); + + gic_cpu_write(&s->gic, 1, 0, 1); + + return 0; +} + +static Property exynos4210_gic_properties[] = { + DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void exynos4210_gic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_gic_init; + dc->props = exynos4210_gic_properties; +} + +static TypeInfo exynos4210_gic_info = { + .name = "exynos4210.gic", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210GicState), + .class_init = exynos4210_gic_class_init, +}; + +static void exynos4210_gic_register_types(void) +{ + type_register_static(&exynos4210_gic_info); +} + +type_init(exynos4210_gic_register_types) + +/* + * IRQGate struct. + * IRQ Gate represents OR gate between GICs to pass IRQ to PIC. + */ +typedef struct { + SysBusDevice busdev; + + qemu_irq pic_irq[NCPU]; /* output IRQs to PICs */ + uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */ +} Exynos4210IRQGateState; + +static const VMStateDescription vmstate_exynos4210_irq_gate = { + .name = "exynos4210.irq_gate", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState, + EXYNOS4210_IRQ_GATE_NINPUTS), + VMSTATE_END_OF_LIST() + } +}; + +/* Process a change in an external IRQ input. */ +static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) +{ + Exynos4210IRQGateState *s = + (Exynos4210IRQGateState *)opaque; + uint32_t odd, even; + + if (irq & 1) { + odd = irq; + even = irq & ~1; + } else { + even = irq; + odd = irq | 1; + } + + assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS); + s->gpio_level[irq] = level; + + if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) { + qemu_irq_raise(s->pic_irq[even >> 1]); + } else { + qemu_irq_lower(s->pic_irq[even >> 1]); + } + + return; +} + +static void exynos4210_irq_gate_reset(DeviceState *d) +{ + Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d; + + memset(&s->gpio_level, 0, sizeof(s->gpio_level)); +} + +/* + * IRQ Gate initialization. + */ +static int exynos4210_irq_gate_init(SysBusDevice *dev) +{ + unsigned int i; + Exynos4210IRQGateState *s = + FROM_SYSBUS(Exynos4210IRQGateState, dev); + + /* Allocate general purpose input signals and connect a handler to each of + * them */ + qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, + EXYNOS4210_IRQ_GATE_NINPUTS); + + /* Connect SysBusDev irqs to device specific irqs */ + for (i = 0; i < NCPU; i++) { + sysbus_init_irq(dev, &s->pic_irq[i]); + } + + return 0; +} + +static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_irq_gate_init; + dc->reset = exynos4210_irq_gate_reset; + dc->vmsd = &vmstate_exynos4210_irq_gate; +} + +static TypeInfo exynos4210_irq_gate_info = { + .name = "exynos4210.irq_gate", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210IRQGateState), + .class_init = exynos4210_irq_gate_class_init, +}; + +static void exynos4210_irq_gate_register_types(void) +{ + type_register_static(&exynos4210_irq_gate_info); +} + +type_init(exynos4210_irq_gate_register_types) diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c new file mode 100644 index 0000000000..01e3fb8a3b --- /dev/null +++ b/hw/exynos4210_mct.c @@ -0,0 +1,1488 @@ +/* + * Samsung exynos4210 Multi Core timer + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin <e.voevodin@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Global Timer: + * + * Consists of two timers. First represents Free Running Counter and second + * is used to measure interval from FRC to nearest comparator. + * + * 0 UINT64_MAX + * | timer0 | + * | <-------------------------------------------------------------- | + * | --------------------------------------------frc---------------> | + * |______________________________________________|__________________| + * CMP0 CMP1 CMP2 | CMP3 + * __| |_ + * | timer1 | + * | -------------> | + * frc CMPx + * + * Problem: when implementing global timer as is, overflow arises. + * next_time = cur_time + period * count; + * period and count are 64 bits width. + * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT + * register during each event. + * + * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because + * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--. + * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0 + * generates IRQs suffers from too frequently events. Better to have one + * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT, + * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values, + * there is no way to avoid frequently events). + */ + +#include "sysbus.h" +#include "qemu-timer.h" +#include "qemu-common.h" +#include "ptimer.h" + +#include "exynos4210.h" + +//#define DEBUG_MCT + +#ifdef DEBUG_MCT +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define MCT_CFG 0x000 +#define G_CNT_L 0x100 +#define G_CNT_U 0x104 +#define G_CNT_WSTAT 0x110 +#define G_COMP0_L 0x200 +#define G_COMP0_U 0x204 +#define G_COMP0_ADD_INCR 0x208 +#define G_COMP1_L 0x210 +#define G_COMP1_U 0x214 +#define G_COMP1_ADD_INCR 0x218 +#define G_COMP2_L 0x220 +#define G_COMP2_U 0x224 +#define G_COMP2_ADD_INCR 0x228 +#define G_COMP3_L 0x230 +#define G_COMP3_U 0x234 +#define G_COMP3_ADD_INCR 0x238 +#define G_TCON 0x240 +#define G_INT_CSTAT 0x244 +#define G_INT_ENB 0x248 +#define G_WSTAT 0x24C +#define L0_TCNTB 0x300 +#define L0_TCNTO 0x304 +#define L0_ICNTB 0x308 +#define L0_ICNTO 0x30C +#define L0_FRCNTB 0x310 +#define L0_FRCNTO 0x314 +#define L0_TCON 0x320 +#define L0_INT_CSTAT 0x330 +#define L0_INT_ENB 0x334 +#define L0_WSTAT 0x340 +#define L1_TCNTB 0x400 +#define L1_TCNTO 0x404 +#define L1_ICNTB 0x408 +#define L1_ICNTO 0x40C +#define L1_FRCNTB 0x410 +#define L1_FRCNTO 0x414 +#define L1_TCON 0x420 +#define L1_INT_CSTAT 0x430 +#define L1_INT_ENB 0x434 +#define L1_WSTAT 0x440 + +#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF) +#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7)) + +#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10) +#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10) + +#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10) +#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10) + +#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10) + +/* MCT bits */ +#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x)) +#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1)) +#define G_TCON_TIMER_ENABLE (1 << 8) + +#define G_INT_ENABLE(x) (1 << (x)) +#define G_INT_CSTAT_COMP(x) (1 << (x)) + +#define G_CNT_WSTAT_L 1 +#define G_CNT_WSTAT_U 2 + +#define G_WSTAT_COMP_L(x) (1 << 4 * (x)) +#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1)) +#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2)) +#define G_WSTAT_TCON_WRITE (1 << 16) + +#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100) +#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \ + (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2) + +#define L_ICNTB_MANUAL_UPDATE (1 << 31) + +#define L_TCON_TICK_START (1) +#define L_TCON_INT_START (1 << 1) +#define L_TCON_INTERVAL_MODE (1 << 2) +#define L_TCON_FRC_START (1 << 3) + +#define L_INT_CSTAT_INTCNT (1 << 0) +#define L_INT_CSTAT_FRCCNT (1 << 1) + +#define L_INT_INTENB_ICNTEIE (1 << 0) +#define L_INT_INTENB_FRCEIE (1 << 1) + +#define L_WSTAT_TCNTB_WRITE (1 << 0) +#define L_WSTAT_ICNTB_WRITE (1 << 1) +#define L_WSTAT_FRCCNTB_WRITE (1 << 2) +#define L_WSTAT_TCON_WRITE (1 << 3) + +enum LocalTimerRegCntIndexes { + L_REG_CNT_TCNTB, + L_REG_CNT_TCNTO, + L_REG_CNT_ICNTB, + L_REG_CNT_ICNTO, + L_REG_CNT_FRCCNTB, + L_REG_CNT_FRCCNTO, + + L_REG_CNT_AMOUNT +}; + +#define MCT_NIRQ 6 +#define MCT_SFR_SIZE 0x444 + +#define MCT_GT_CMP_NUM 4 + +#define MCT_GT_MAX_VAL UINT64_MAX + +#define MCT_GT_COUNTER_STEP 0x100000000ULL +#define MCT_LT_COUNTER_STEP 0x100000000ULL +#define MCT_LT_CNT_LOW_LIMIT 0x100 + +/* global timer */ +typedef struct { + qemu_irq irq[MCT_GT_CMP_NUM]; + + struct gregs { + uint64_t cnt; + uint32_t cnt_wstat; + uint32_t tcon; + uint32_t int_cstat; + uint32_t int_enb; + uint32_t wstat; + uint64_t comp[MCT_GT_CMP_NUM]; + uint32_t comp_add_incr[MCT_GT_CMP_NUM]; + } reg; + + uint64_t count; /* Value FRC was armed with */ + int32_t curr_comp; /* Current comparator FRC is running to */ + + ptimer_state *ptimer_frc; /* FRC timer */ + +} Exynos4210MCTGT; + +/* local timer */ +typedef struct { + int id; /* timer id */ + qemu_irq irq; /* local timer irq */ + + struct tick_timer { + uint32_t cnt_run; /* cnt timer is running */ + uint32_t int_run; /* int timer is running */ + + uint32_t last_icnto; + uint32_t last_tcnto; + uint32_t tcntb; /* initial value for TCNTB */ + uint32_t icntb; /* initial value for ICNTB */ + + /* for step mode */ + uint64_t distance; /* distance to count to the next event */ + uint64_t progress; /* progress when counting by steps */ + uint64_t count; /* count to arm timer with */ + + ptimer_state *ptimer_tick; /* timer for tick counter */ + } tick_timer; + + /* use ptimer.c to represent count down timer */ + + ptimer_state *ptimer_frc; /* timer for free running counter */ + + /* registers */ + struct lregs { + uint32_t cnt[L_REG_CNT_AMOUNT]; + uint32_t tcon; + uint32_t int_cstat; + uint32_t int_enb; + uint32_t wstat; + } reg; + +} Exynos4210MCTLT; + +typedef struct Exynos4210MCTState { + SysBusDevice busdev; + MemoryRegion iomem; + + /* Registers */ + uint32_t reg_mct_cfg; + + Exynos4210MCTLT l_timer[2]; + Exynos4210MCTGT g_timer; + + uint32_t freq; /* all timers tick frequency, TCLK */ +} Exynos4210MCTState; + +/*** VMState ***/ +static const VMStateDescription vmstate_tick_timer = { + .name = "exynos4210.mct.tick_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cnt_run, struct tick_timer), + VMSTATE_UINT32(int_run, struct tick_timer), + VMSTATE_UINT32(last_icnto, struct tick_timer), + VMSTATE_UINT32(last_tcnto, struct tick_timer), + VMSTATE_UINT32(tcntb, struct tick_timer), + VMSTATE_UINT32(icntb, struct tick_timer), + VMSTATE_UINT64(distance, struct tick_timer), + VMSTATE_UINT64(progress, struct tick_timer), + VMSTATE_UINT64(count, struct tick_timer), + VMSTATE_PTIMER(ptimer_tick, struct tick_timer), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_lregs = { + .name = "exynos4210.mct.lregs", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), + VMSTATE_UINT32(tcon, struct lregs), + VMSTATE_UINT32(int_cstat, struct lregs), + VMSTATE_UINT32(int_enb, struct lregs), + VMSTATE_UINT32(wstat, struct lregs), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_lt = { + .name = "exynos4210.mct.lt", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, Exynos4210MCTLT), + VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, + vmstate_tick_timer, + struct tick_timer), + VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT), + VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0, + vmstate_lregs, + struct lregs), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_gregs = { + .name = "exynos4210.mct.lregs", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(cnt, struct gregs), + VMSTATE_UINT32(cnt_wstat, struct gregs), + VMSTATE_UINT32(tcon, struct gregs), + VMSTATE_UINT32(int_cstat, struct gregs), + VMSTATE_UINT32(int_enb, struct gregs), + VMSTATE_UINT32(wstat, struct gregs), + VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM), + VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs, + MCT_GT_CMP_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_gt = { + .name = "exynos4210.mct.lt", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, + struct gregs), + VMSTATE_UINT64(count, Exynos4210MCTGT), + VMSTATE_INT32(curr_comp, Exynos4210MCTGT), + VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_state = { + .name = "exynos4210.mct", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), + VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, + vmstate_exynos4210_mct_lt, Exynos4210MCTLT), + VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0, + vmstate_exynos4210_mct_gt, Exynos4210MCTGT), + VMSTATE_UINT32(freq, Exynos4210MCTState), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4210_mct_update_freq(Exynos4210MCTState *s); + +/* + * Set counter of FRC global timer. + */ +static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) +{ + s->count = count; + DPRINTF("global timer frc set count 0x%llx\n", count); + ptimer_set_count(s->ptimer_frc, count); +} + +/* + * Get counter of FRC global timer. + */ +static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) +{ + uint64_t count = 0; + count = ptimer_get_count(s->ptimer_frc); + if (!count) { + /* Timer event was generated and s->reg.cnt holds adequate value */ + return s->reg.cnt; + } + count = s->count - count; + return s->reg.cnt + count; +} + +/* + * Stop global FRC timer + */ +static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) +{ + DPRINTF("global timer frc stop\n"); + + ptimer_stop(s->ptimer_frc); +} + +/* + * Start global FRC timer + */ +static void exynos4210_gfrc_start(Exynos4210MCTGT *s) +{ + DPRINTF("global timer frc start\n"); + + ptimer_run(s->ptimer_frc, 1); +} + +/* + * Find next nearest Comparator. If current Comparator value equals to other + * Comparator value, skip them both + */ +static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) +{ + int res; + int i; + int enabled; + uint64_t min; + int min_comp_i; + uint64_t gfrc; + uint64_t distance; + uint64_t distance_min; + int comp_i; + + /* get gfrc count */ + gfrc = exynos4210_gfrc_get_count(&s->g_timer); + + min = UINT64_MAX; + distance_min = UINT64_MAX; + comp_i = MCT_GT_CMP_NUM; + min_comp_i = MCT_GT_CMP_NUM; + enabled = 0; + + /* lookup for nearest comparator */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + + if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) { + + enabled = 1; + + if (s->g_timer.reg.comp[i] > gfrc) { + /* Comparator is upper then FRC */ + distance = s->g_timer.reg.comp[i] - gfrc; + + if (distance <= distance_min) { + distance_min = distance; + comp_i = i; + } + } else { + /* Comparator is below FRC, find the smallest */ + + if (s->g_timer.reg.comp[i] <= min) { + min = s->g_timer.reg.comp[i]; + min_comp_i = i; + } + } + } + } + + if (!enabled) { + /* All Comparators disabled */ + res = -1; + } else if (comp_i < MCT_GT_CMP_NUM) { + /* Found upper Comparator */ + res = comp_i; + } else { + /* All Comparators are below or equal to FRC */ + res = min_comp_i; + } + + DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", + res, + s->g_timer.reg.comp[res], + distance_min, + gfrc); + + return res; +} + +/* + * Get distance to nearest Comparator + */ +static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) +{ + if (id == -1) { + /* no enabled Comparators, choose max distance */ + return MCT_GT_COUNTER_STEP; + } + if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) { + return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt; + } else { + return MCT_GT_COUNTER_STEP; + } +} + +/* + * Restart global FRC timer + */ +static void exynos4210_gfrc_restart(Exynos4210MCTState *s) +{ + uint64_t distance; + + exynos4210_gfrc_stop(&s->g_timer); + + s->g_timer.curr_comp = exynos4210_gcomp_find(s); + + distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); + + if (distance > MCT_GT_COUNTER_STEP || !distance) { + distance = MCT_GT_COUNTER_STEP; + } + + exynos4210_gfrc_set_count(&s->g_timer, distance); + exynos4210_gfrc_start(&s->g_timer); +} + +/* + * Raise global timer CMP IRQ + */ +static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id) +{ + Exynos4210MCTGT *s = opaque; + + /* If CSTAT is pending and IRQ is enabled */ + if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) && + (s->reg.int_enb & G_INT_ENABLE(id))) { + DPRINTF("gcmp timer[%d] IRQ\n", id); + qemu_irq_raise(s->irq[id]); + } +} + +/* + * Lower global timer CMP IRQ + */ +static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id) +{ + Exynos4210MCTGT *s = opaque; + qemu_irq_lower(s->irq[id]); +} + +/* + * Global timer FRC event handler. + * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP + * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value + */ +static void exynos4210_gfrc_event(void *opaque) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int i; + uint64_t distance; + + DPRINTF("\n"); + + s->g_timer.reg.cnt += s->g_timer.count; + + /* Process all comparators */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + + if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) { + /* reached nearest comparator */ + + s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i); + + /* Auto increment */ + if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) { + s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i]; + } + + /* IRQ */ + exynos4210_gcomp_raise_irq(&s->g_timer, i); + } + } + + /* Reload FRC to reach nearest comparator */ + s->g_timer.curr_comp = exynos4210_gcomp_find(s); + distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); + if (distance > MCT_GT_COUNTER_STEP) { + distance = MCT_GT_COUNTER_STEP; + } + exynos4210_gfrc_set_count(&s->g_timer, distance); + + exynos4210_gfrc_start(&s->g_timer); + + return; +} + +/* + * Get counter of FRC local timer. + */ +static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) +{ + return ptimer_get_count(s->ptimer_frc); +} + +/* + * Set counter of FRC local timer. + */ +static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) +{ + if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) { + ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP); + } else { + ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]); + } +} + +/* + * Start local FRC timer + */ +static void exynos4210_lfrc_start(Exynos4210MCTLT *s) +{ + ptimer_run(s->ptimer_frc, 1); +} + +/* + * Stop local FRC timer + */ +static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) +{ + ptimer_stop(s->ptimer_frc); +} + +/* + * Local timer free running counter tick handler + */ +static void exynos4210_lfrc_event(void *opaque) +{ + Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; + + /* local frc expired */ + + DPRINTF("\n"); + + s->reg.int_cstat |= L_INT_CSTAT_FRCCNT; + + /* update frc counter */ + exynos4210_lfrc_update_count(s); + + /* raise irq */ + if (s->reg.int_enb & L_INT_INTENB_FRCEIE) { + qemu_irq_raise(s->irq); + } + + /* we reached here, this means that timer is enabled */ + exynos4210_lfrc_start(s); +} + +static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s); +static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s); +static void exynos4210_ltick_recalc_count(struct tick_timer *s); + +/* + * Action on enabling local tick int timer + */ +static void exynos4210_ltick_int_start(struct tick_timer *s) +{ + if (!s->int_run) { + s->int_run = 1; + } +} + +/* + * Action on disabling local tick int timer + */ +static void exynos4210_ltick_int_stop(struct tick_timer *s) +{ + if (s->int_run) { + s->last_icnto = exynos4210_ltick_int_get_cnto(s); + s->int_run = 0; + } +} + +/* + * Get count for INT timer + */ +static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) +{ + uint32_t icnto; + uint64_t remain; + uint64_t count; + uint64_t counted; + uint64_t cur_progress; + + count = ptimer_get_count(s->ptimer_tick); + if (count) { + /* timer is still counting, called not from event */ + counted = s->count - ptimer_get_count(s->ptimer_tick); + cur_progress = s->progress + counted; + } else { + /* timer expired earlier */ + cur_progress = s->progress; + } + + remain = s->distance - cur_progress; + + if (!s->int_run) { + /* INT is stopped. */ + icnto = s->last_icnto; + } else { + /* Both are counting */ + icnto = remain / s->tcntb; + } + + return icnto; +} + +/* + * Start local tick cnt timer. + */ +static void exynos4210_ltick_cnt_start(struct tick_timer *s) +{ + if (!s->cnt_run) { + + exynos4210_ltick_recalc_count(s); + ptimer_set_count(s->ptimer_tick, s->count); + ptimer_run(s->ptimer_tick, 1); + + s->cnt_run = 1; + } +} + +/* + * Stop local tick cnt timer. + */ +static void exynos4210_ltick_cnt_stop(struct tick_timer *s) +{ + if (s->cnt_run) { + + s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s); + + if (s->int_run) { + exynos4210_ltick_int_stop(s); + } + + ptimer_stop(s->ptimer_tick); + + s->cnt_run = 0; + } +} + +/* + * Get counter for CNT timer + */ +static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) +{ + uint32_t tcnto; + uint32_t icnto; + uint64_t remain; + uint64_t counted; + uint64_t count; + uint64_t cur_progress; + + count = ptimer_get_count(s->ptimer_tick); + if (count) { + /* timer is still counting, called not from event */ + counted = s->count - ptimer_get_count(s->ptimer_tick); + cur_progress = s->progress + counted; + } else { + /* timer expired earlier */ + cur_progress = s->progress; + } + + remain = s->distance - cur_progress; + + if (!s->cnt_run) { + /* Both are stopped. */ + tcnto = s->last_tcnto; + } else if (!s->int_run) { + /* INT counter is stopped, progress is by CNT timer */ + tcnto = remain % s->tcntb; + } else { + /* Both are counting */ + icnto = remain / s->tcntb; + if (icnto) { + tcnto = remain % (icnto * s->tcntb); + } else { + tcnto = remain % s->tcntb; + } + } + + return tcnto; +} + +/* + * Set new values of counters for CNT and INT timers + */ +static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, + uint32_t new_int) +{ + uint32_t cnt_stopped = 0; + uint32_t int_stopped = 0; + + if (s->cnt_run) { + exynos4210_ltick_cnt_stop(s); + cnt_stopped = 1; + } + + if (s->int_run) { + exynos4210_ltick_int_stop(s); + int_stopped = 1; + } + + s->tcntb = new_cnt + 1; + s->icntb = new_int + 1; + + if (cnt_stopped) { + exynos4210_ltick_cnt_start(s); + } + if (int_stopped) { + exynos4210_ltick_int_start(s); + } + +} + +/* + * Calculate new counter value for tick timer + */ +static void exynos4210_ltick_recalc_count(struct tick_timer *s) +{ + uint64_t to_count; + + if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) { + /* + * one or both timers run and not counted to the end; + * distance is not passed, recalculate with last_tcnto * last_icnto + */ + + if (s->last_tcnto) { + to_count = s->last_tcnto * s->last_icnto; + } else { + to_count = s->last_icnto; + } + } else { + /* distance is passed, recalculate with tcnto * icnto */ + if (s->icntb) { + s->distance = s->tcntb * s->icntb; + } else { + s->distance = s->tcntb; + } + + to_count = s->distance; + s->progress = 0; + } + + if (to_count > MCT_LT_COUNTER_STEP) { + /* count by step */ + s->count = MCT_LT_COUNTER_STEP; + } else { + s->count = to_count; + } +} + +/* + * Initialize tick_timer + */ +static void exynos4210_ltick_timer_init(struct tick_timer *s) +{ + exynos4210_ltick_int_stop(s); + exynos4210_ltick_cnt_stop(s); + + s->count = 0; + s->distance = 0; + s->progress = 0; + s->icntb = 0; + s->tcntb = 0; +} + +/* + * tick_timer event. + * Raises when abstract tick_timer expires. + */ +static void exynos4210_ltick_timer_event(struct tick_timer *s) +{ + s->progress += s->count; +} + +/* + * Local timer tick counter handler. + * Don't use reloaded timers. If timer counter = zero + * then handler called but after handler finished no + * timer reload occurs. + */ +static void exynos4210_ltick_event(void *opaque) +{ + Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; + uint32_t tcnto; + uint32_t icnto; +#ifdef DEBUG_MCT + static uint64_t time1[2] = {0}; + static uint64_t time2[2] = {0}; +#endif + + /* Call tick_timer event handler, it will update it's tcntb and icntb */ + exynos4210_ltick_timer_event(&s->tick_timer); + + /* get tick_timer cnt */ + tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer); + + /* get tick_timer int */ + icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer); + + /* raise IRQ if needed */ + if (!icnto && s->reg.tcon & L_TCON_INT_START) { + /* INT counter enabled and expired */ + + s->reg.int_cstat |= L_INT_CSTAT_INTCNT; + + /* raise interrupt if enabled */ + if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) { +#ifdef DEBUG_MCT + time2[s->id] = qemu_get_clock_ns(vm_clock); + DPRINTF("local timer[%d] IRQ: %llx\n", s->id, + time2[s->id] - time1[s->id]); + time1[s->id] = time2[s->id]; +#endif + qemu_irq_raise(s->irq); + } + + /* reload ICNTB */ + if (s->reg.tcon & L_TCON_INTERVAL_MODE) { + exynos4210_ltick_set_cntb(&s->tick_timer, + s->reg.cnt[L_REG_CNT_TCNTB], + s->reg.cnt[L_REG_CNT_ICNTB]); + } + } else { + /* reload TCNTB */ + if (!tcnto) { + exynos4210_ltick_set_cntb(&s->tick_timer, + s->reg.cnt[L_REG_CNT_TCNTB], + icnto); + } + } + + /* start tick_timer cnt */ + exynos4210_ltick_cnt_start(&s->tick_timer); + + /* start tick_timer int */ + exynos4210_ltick_int_start(&s->tick_timer); +} + +/* update timer frequency */ +static void exynos4210_mct_update_freq(Exynos4210MCTState *s) +{ + uint32_t freq = s->freq; + s->freq = 24000000 / + ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) * + MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); + + if (freq != s->freq) { + DPRINTF("freq=%dHz\n", s->freq); + + /* global timer */ + ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); + + /* local timer */ + ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); + ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); + ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); + ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); + } +} + +/* set defaul_timer values for all fields */ +static void exynos4210_mct_reset(DeviceState *d) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)d; + uint32_t i; + + s->reg_mct_cfg = 0; + + /* global timer */ + memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); + exynos4210_gfrc_stop(&s->g_timer); + + /* local timer */ + memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); + memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt)); + for (i = 0; i < 2; i++) { + s->l_timer[i].reg.int_cstat = 0; + s->l_timer[i].reg.int_enb = 0; + s->l_timer[i].reg.tcon = 0; + s->l_timer[i].reg.wstat = 0; + s->l_timer[i].tick_timer.count = 0; + s->l_timer[i].tick_timer.distance = 0; + s->l_timer[i].tick_timer.progress = 0; + ptimer_stop(s->l_timer[i].ptimer_frc); + + exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); + } + + exynos4210_mct_update_freq(s); + +} + +/* Multi Core Timer read */ +static uint64_t exynos4210_mct_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int index; + int shift; + uint64_t count; + uint32_t value; + int lt_i; + + switch (offset) { + + case MCT_CFG: + value = s->reg_mct_cfg; + break; + + case G_CNT_L: case G_CNT_U: + shift = 8 * (offset & 0x4); + count = exynos4210_gfrc_get_count(&s->g_timer); + value = UINT32_MAX & (count >> shift); + DPRINTF("read FRC=0x%llx\n", count); + break; + + case G_CNT_WSTAT: + value = s->g_timer.reg.cnt_wstat; + break; + + case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): + case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); + break; + + case G_TCON: + value = s->g_timer.reg.tcon; + break; + + case G_INT_CSTAT: + value = s->g_timer.reg.int_cstat; + break; + + case G_INT_ENB: + value = s->g_timer.reg.int_enb; + break; + break; + case G_WSTAT: + value = s->g_timer.reg.wstat; + break; + + case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: + case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: + value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)]; + break; + + /* Local timers */ + case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB: + case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB: + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + value = s->l_timer[lt_i].reg.cnt[index]; + break; + + case L0_TCNTO: case L1_TCNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer); + DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value); + break; + + case L0_ICNTO: case L1_ICNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer); + DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value); + break; + + case L0_FRCNTO: case L1_FRCNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); + + break; + + case L0_TCON: case L1_TCON: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.tcon; + break; + + case L0_INT_CSTAT: case L1_INT_CSTAT: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.int_cstat; + break; + + case L0_INT_ENB: case L1_INT_ENB: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.int_enb; + break; + + case L0_WSTAT: case L1_WSTAT: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.wstat; + break; + + default: + hw_error("exynos4210.mct: bad read offset " + TARGET_FMT_plx "\n", offset); + break; + } + return value; +} + +/* MCT write */ +static void exynos4210_mct_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int index; /* index in buffer which represents register set */ + int shift; + int lt_i; + uint64_t new_frc; + uint32_t i; + uint32_t old_val; +#ifdef DEBUG_MCT + static uint32_t icntb_max[2] = {0}; + static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX}; + static uint32_t tcntb_max[2] = {0}; + static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX}; +#endif + + new_frc = s->g_timer.reg.cnt; + + switch (offset) { + + case MCT_CFG: + s->reg_mct_cfg = value; + exynos4210_mct_update_freq(s); + break; + + case G_CNT_L: + case G_CNT_U: + if (offset == G_CNT_L) { + + DPRINTF("global timer write to reg.cntl %llx\n", value); + + new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value; + s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L; + } + if (offset == G_CNT_U) { + + DPRINTF("global timer write to reg.cntu %llx\n", value); + + new_frc = (s->g_timer.reg.cnt & UINT32_MAX) + + ((uint64_t)value << 32); + s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U; + } + + s->g_timer.reg.cnt = new_frc; + exynos4210_gfrc_restart(s); + break; + + case G_CNT_WSTAT: + s->g_timer.reg.cnt_wstat &= ~(value); + break; + + case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): + case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + s->g_timer.reg.comp[index] = + (s->g_timer.reg.comp[index] & + (((uint64_t)UINT32_MAX << 32) >> shift)) + + (value << shift); + + DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); + + if (offset&0x4) { + s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); + } else { + s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); + } + + exynos4210_gfrc_restart(s); + break; + + case G_TCON: + old_val = s->g_timer.reg.tcon; + s->g_timer.reg.tcon = value; + s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE; + + DPRINTF("global timer write to reg.g_tcon %llx\n", value); + + /* Start FRC if transition from disabled to enabled */ + if ((value & G_TCON_TIMER_ENABLE) > (old_val & + G_TCON_TIMER_ENABLE)) { + exynos4210_gfrc_start(&s->g_timer); + } + if ((value & G_TCON_TIMER_ENABLE) < (old_val & + G_TCON_TIMER_ENABLE)) { + exynos4210_gfrc_stop(&s->g_timer); + } + + /* Start CMP if transition from disabled to enabled */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if ((value & G_TCON_COMP_ENABLE(i)) != (old_val & + G_TCON_COMP_ENABLE(i))) { + exynos4210_gfrc_restart(s); + } + } + break; + + case G_INT_CSTAT: + s->g_timer.reg.int_cstat &= ~(value); + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if (value & G_INT_CSTAT_COMP(i)) { + exynos4210_gcomp_lower_irq(&s->g_timer, i); + } + } + break; + + case G_INT_ENB: + + /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & + G_INT_ENABLE(i))) { + if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) { + exynos4210_gcomp_raise_irq(&s->g_timer, i); + } + } + + if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon & + G_INT_ENABLE(i))) { + exynos4210_gcomp_lower_irq(&s->g_timer, i); + } + } + + DPRINTF("global timer INT enable %llx\n", value); + s->g_timer.reg.int_enb = value; + break; + + case G_WSTAT: + s->g_timer.reg.wstat &= ~(value); + break; + + case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: + case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: + index = GET_G_COMP_ADD_INCR_IDX(offset); + s->g_timer.reg.comp_add_incr[index] = value; + s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index); + break; + + /* Local timers */ + case L0_TCON: case L1_TCON: + lt_i = GET_L_TIMER_IDX(offset); + old_val = s->l_timer[lt_i].reg.tcon; + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; + s->l_timer[lt_i].reg.tcon = value; + + /* Stop local CNT */ + if ((value & L_TCON_TICK_START) < + (old_val & L_TCON_TICK_START)) { + DPRINTF("local timer[%d] stop cnt\n", lt_i); + exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer); + } + + /* Stop local INT */ + if ((value & L_TCON_INT_START) < + (old_val & L_TCON_INT_START)) { + DPRINTF("local timer[%d] stop int\n", lt_i); + exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer); + } + + /* Start local CNT */ + if ((value & L_TCON_TICK_START) > + (old_val & L_TCON_TICK_START)) { + DPRINTF("local timer[%d] start cnt\n", lt_i); + exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer); + } + + /* Start local INT */ + if ((value & L_TCON_INT_START) > + (old_val & L_TCON_INT_START)) { + DPRINTF("local timer[%d] start int\n", lt_i); + exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); + } + + /* Start or Stop local FRC if TCON changed */ + if ((value & L_TCON_FRC_START) > + (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { + DPRINTF("local timer[%d] start frc\n", lt_i); + exynos4210_lfrc_start(&s->l_timer[lt_i]); + } + if ((value & L_TCON_FRC_START) < + (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { + DPRINTF("local timer[%d] stop frc\n", lt_i); + exynos4210_lfrc_stop(&s->l_timer[lt_i]); + } + break; + + case L0_TCNTB: case L1_TCNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + /* + * TCNTB is updated to internal register only after CNT expired. + * Due to this we should reload timer to nearest moment when CNT is + * expired and then in event handler update tcntb to new TCNTB value. + */ + exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, + s->l_timer[lt_i].tick_timer.icntb); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; + +#ifdef DEBUG_MCT + if (tcntb_min[lt_i] > value) { + tcntb_min[lt_i] = value; + } + if (tcntb_max[lt_i] < value) { + tcntb_max[lt_i] = value; + } + DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n", + lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]); +#endif + break; + + case L0_ICNTB: case L1_ICNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value & + ~L_ICNTB_MANUAL_UPDATE; + + /* + * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event + * could raise too fast disallowing QEMU to execute target code. + */ + if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] * + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) { + if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) { + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = + MCT_LT_CNT_LOW_LIMIT; + } else { + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = + MCT_LT_CNT_LOW_LIMIT / + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]; + } + } + + if (value & L_ICNTB_MANUAL_UPDATE) { + exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, + s->l_timer[lt_i].tick_timer.tcntb, + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]); + } + +#ifdef DEBUG_MCT + if (icntb_min[lt_i] > value) { + icntb_min[lt_i] = value; + } + if (icntb_max[lt_i] < value) { + icntb_max[lt_i] = value; + } +DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", + lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); +#endif +break; + + case L0_FRCNTB: case L1_FRCNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value; + + break; + + case L0_TCNTO: case L1_TCNTO: + case L0_ICNTO: case L1_ICNTO: + case L0_FRCNTO: case L1_FRCNTO: + fprintf(stderr, "\n[exynos4210.mct: write to RO register " + TARGET_FMT_plx "]\n\n", offset); + break; + + case L0_INT_CSTAT: case L1_INT_CSTAT: + lt_i = GET_L_TIMER_IDX(offset); + + DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value); + + s->l_timer[lt_i].reg.int_cstat &= ~value; + if (!s->l_timer[lt_i].reg.int_cstat) { + qemu_irq_lower(s->l_timer[lt_i].irq); + } + break; + + case L0_INT_ENB: case L1_INT_ENB: + lt_i = GET_L_TIMER_IDX(offset); + old_val = s->l_timer[lt_i].reg.int_enb; + + /* Raise Local timer IRQ if cstat is pending */ + if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) { + if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) { + qemu_irq_raise(s->l_timer[lt_i].irq); + } + } + + s->l_timer[lt_i].reg.int_enb = value; + + break; + + case L0_WSTAT: case L1_WSTAT: + lt_i = GET_L_TIMER_IDX(offset); + + s->l_timer[lt_i].reg.wstat &= ~value; + break; + + default: + hw_error("exynos4210.mct: bad write offset " + TARGET_FMT_plx "\n", offset); + break; + } +} + +static const MemoryRegionOps exynos4210_mct_ops = { + .read = exynos4210_mct_read, + .write = exynos4210_mct_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* MCT init */ +static int exynos4210_mct_init(SysBusDevice *dev) +{ + int i; + Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev); + QEMUBH *bh[2]; + + /* Global timer */ + bh[0] = qemu_bh_new(exynos4210_gfrc_event, s); + s->g_timer.ptimer_frc = ptimer_init(bh[0]); + memset(&s->g_timer.reg, 0, sizeof(struct gregs)); + + /* Local timers */ + for (i = 0; i < 2; i++) { + bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]); + bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]); + s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]); + s->l_timer[i].ptimer_frc = ptimer_init(bh[1]); + s->l_timer[i].id = i; + } + + /* IRQs */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + sysbus_init_irq(dev, &s->g_timer.irq[i]); + } + for (i = 0; i < 2; i++) { + sysbus_init_irq(dev, &s->l_timer[i].irq); + } + + memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct", + MCT_SFR_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void exynos4210_mct_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_mct_init; + dc->reset = exynos4210_mct_reset; + dc->vmsd = &vmstate_exynos4210_mct_state; +} + +static TypeInfo exynos4210_mct_info = { + .name = "exynos4210.mct", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210MCTState), + .class_init = exynos4210_mct_class_init, +}; + +static void exynos4210_mct_register_types(void) +{ + type_register_static(&exynos4210_mct_info); +} + +type_init(exynos4210_mct_register_types) diff --git a/hw/exynos4210_pmu.c b/hw/exynos4210_pmu.c new file mode 100644 index 0000000000..c12d7501cc --- /dev/null +++ b/hw/exynos4210_pmu.c @@ -0,0 +1,499 @@ +/* + * Exynos4210 Power Management Unit (PMU) Emulation + * + * Copyright (C) 2011 Samsung Electronics Co Ltd. + * Maksim Kozlov <m.kozlov@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * This model implements PMU registers just as a bulk of memory. Currently, + * the only reason this device exists is that secondary CPU boot loader + * uses PMU INFORM5 register as a holding pen. + */ + +#include "sysbus.h" + +#ifndef DEBUG_PMU +#define DEBUG_PMU 0 +#endif + +#ifndef DEBUG_PMU_EXTEND +#define DEBUG_PMU_EXTEND 0 +#endif + +#if DEBUG_PMU +#define PRINT_DEBUG(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) + +#if DEBUG_PMU_EXTEND +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) +#else +#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0) +#endif /* EXTEND */ + +#else +#define PRINT_DEBUG(fmt, args...) do {} while (0) +#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0) +#endif + +/* + * Offsets for PMU registers + */ +#define OM_STAT 0x0000 /* OM status register */ +#define RTC_CLKO_SEL 0x000C /* Controls RTCCLKOUT */ +#define GNSS_RTC_OUT_CTRL 0x0010 /* Controls GNSS_RTC_OUT */ +/* Decides whether system-level low-power mode is used. */ +#define SYSTEM_POWER_DOWN_CTRL 0x0200 +/* Sets control options for CENTRAL_SEQ */ +#define SYSTEM_POWER_DOWN_OPTION 0x0208 +#define SWRESET 0x0400 /* Generate software reset */ +#define RST_STAT 0x0404 /* Reset status register */ +#define WAKEUP_STAT 0x0600 /* Wakeup status register */ +#define EINT_WAKEUP_MASK 0x0604 /* Configure External INTerrupt mask */ +#define WAKEUP_MASK 0x0608 /* Configure wakeup source mask */ +#define HDMI_PHY_CONTROL 0x0700 /* HDMI PHY control register */ +#define USBDEVICE_PHY_CONTROL 0x0704 /* USB Device PHY control register */ +#define USBHOST_PHY_CONTROL 0x0708 /* USB HOST PHY control register */ +#define DAC_PHY_CONTROL 0x070C /* DAC control register */ +#define MIPI_PHY0_CONTROL 0x0710 /* MIPI PHY control register */ +#define MIPI_PHY1_CONTROL 0x0714 /* MIPI PHY control register */ +#define ADC_PHY_CONTROL 0x0718 /* TS-ADC control register */ +#define PCIe_PHY_CONTROL 0x071C /* TS-PCIe control register */ +#define SATA_PHY_CONTROL 0x0720 /* TS-SATA control register */ +#define INFORM0 0x0800 /* Information register 0 */ +#define INFORM1 0x0804 /* Information register 1 */ +#define INFORM2 0x0808 /* Information register 2 */ +#define INFORM3 0x080C /* Information register 3 */ +#define INFORM4 0x0810 /* Information register 4 */ +#define INFORM5 0x0814 /* Information register 5 */ +#define INFORM6 0x0818 /* Information register 6 */ +#define INFORM7 0x081C /* Information register 7 */ +#define PMU_DEBUG 0x0A00 /* PMU debug register */ +/* Registers to set system-level low-power option */ +#define ARM_CORE0_SYS_PWR_REG 0x1000 +#define ARM_CORE1_SYS_PWR_REG 0x1010 +#define ARM_COMMON_SYS_PWR_REG 0x1080 +#define ARM_CPU_L2_0_SYS_PWR_REG 0x10C0 +#define ARM_CPU_L2_1_SYS_PWR_REG 0x10C4 +#define CMU_ACLKSTOP_SYS_PWR_REG 0x1100 +#define CMU_SCLKSTOP_SYS_PWR_REG 0x1104 +#define CMU_RESET_SYS_PWR_REG 0x110C +#define APLL_SYSCLK_SYS_PWR_REG 0x1120 +#define MPLL_SYSCLK_SYS_PWR_REG 0x1124 +#define VPLL_SYSCLK_SYS_PWR_REG 0x1128 +#define EPLL_SYSCLK_SYS_PWR_REG 0x112C +#define CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG 0x1138 +#define CMU_RESET_GPS_ALIVE_SYS_PWR_REG 0x113C +#define CMU_CLKSTOP_CAM_SYS_PWR_REG 0x1140 +#define CMU_CLKSTOP_TV_SYS_PWR_REG 0x1144 +#define CMU_CLKSTOP_MFC_SYS_PWR_REG 0x1148 +#define CMU_CLKSTOP_G3D_SYS_PWR_REG 0x114C +#define CMU_CLKSTOP_LCD0_SYS_PWR_REG 0x1150 +#define CMU_CLKSTOP_LCD1_SYS_PWR_REG 0x1154 +#define CMU_CLKSTOP_MAUDIO_SYS_PWR_REG 0x1158 +#define CMU_CLKSTOP_GPS_SYS_PWR_REG 0x115C +#define CMU_RESET_CAM_SYS_PWR_REG 0x1160 +#define CMU_RESET_TV_SYS_PWR_REG 0x1164 +#define CMU_RESET_MFC_SYS_PWR_REG 0x1168 +#define CMU_RESET_G3D_SYS_PWR_REG 0x116C +#define CMU_RESET_LCD0_SYS_PWR_REG 0x1170 +#define CMU_RESET_LCD1_SYS_PWR_REG 0x1174 +#define CMU_RESET_MAUDIO_SYS_PWR_REG 0x1178 +#define CMU_RESET_GPS_SYS_PWR_REG 0x117C +#define TOP_BUS_SYS_PWR_REG 0x1180 +#define TOP_RETENTION_SYS_PWR_REG 0x1184 +#define TOP_PWR_SYS_PWR_REG 0x1188 +#define LOGIC_RESET_SYS_PWR_REG 0x11A0 +#define OneNANDXL_MEM_SYS_PWR_REG 0x11C0 +#define MODEMIF_MEM_SYS_PWR_REG 0x11C4 +#define USBDEVICE_MEM_SYS_PWR_REG 0x11CC +#define SDMMC_MEM_SYS_PWR_REG 0x11D0 +#define CSSYS_MEM_SYS_PWR_REG 0x11D4 +#define SECSS_MEM_SYS_PWR_REG 0x11D8 +#define PCIe_MEM_SYS_PWR_REG 0x11E0 +#define SATA_MEM_SYS_PWR_REG 0x11E4 +#define PAD_RETENTION_DRAM_SYS_PWR_REG 0x1200 +#define PAD_RETENTION_MAUDIO_SYS_PWR_REG 0x1204 +#define PAD_RETENTION_GPIO_SYS_PWR_REG 0x1220 +#define PAD_RETENTION_UART_SYS_PWR_REG 0x1224 +#define PAD_RETENTION_MMCA_SYS_PWR_REG 0x1228 +#define PAD_RETENTION_MMCB_SYS_PWR_REG 0x122C +#define PAD_RETENTION_EBIA_SYS_PWR_REG 0x1230 +#define PAD_RETENTION_EBIB_SYS_PWR_REG 0x1234 +#define PAD_ISOLATION_SYS_PWR_REG 0x1240 +#define PAD_ALV_SEL_SYS_PWR_REG 0x1260 +#define XUSBXTI_SYS_PWR_REG 0x1280 +#define XXTI_SYS_PWR_REG 0x1284 +#define EXT_REGULATOR_SYS_PWR_REG 0x12C0 +#define GPIO_MODE_SYS_PWR_REG 0x1300 +#define GPIO_MODE_MAUDIO_SYS_PWR_REG 0x1340 +#define CAM_SYS_PWR_REG 0x1380 +#define TV_SYS_PWR_REG 0x1384 +#define MFC_SYS_PWR_REG 0x1388 +#define G3D_SYS_PWR_REG 0x138C +#define LCD0_SYS_PWR_REG 0x1390 +#define LCD1_SYS_PWR_REG 0x1394 +#define MAUDIO_SYS_PWR_REG 0x1398 +#define GPS_SYS_PWR_REG 0x139C +#define GPS_ALIVE_SYS_PWR_REG 0x13A0 +#define ARM_CORE0_CONFIGURATION 0x2000 /* Configure power mode of ARM_CORE0 */ +#define ARM_CORE0_STATUS 0x2004 /* Check power mode of ARM_CORE0 */ +#define ARM_CORE0_OPTION 0x2008 /* Sets control options for ARM_CORE0 */ +#define ARM_CORE1_CONFIGURATION 0x2080 /* Configure power mode of ARM_CORE1 */ +#define ARM_CORE1_STATUS 0x2084 /* Check power mode of ARM_CORE1 */ +#define ARM_CORE1_OPTION 0x2088 /* Sets control options for ARM_CORE0 */ +#define ARM_COMMON_OPTION 0x2408 /* Sets control options for ARM_COMMON */ +/* Configure power mode of ARM_CPU_L2_0 */ +#define ARM_CPU_L2_0_CONFIGURATION 0x2600 +#define ARM_CPU_L2_0_STATUS 0x2604 /* Check power mode of ARM_CPU_L2_0 */ +/* Configure power mode of ARM_CPU_L2_1 */ +#define ARM_CPU_L2_1_CONFIGURATION 0x2620 +#define ARM_CPU_L2_1_STATUS 0x2624 /* Check power mode of ARM_CPU_L2_1 */ +/* Sets control options for PAD_RETENTION_MAUDIO */ +#define PAD_RETENTION_MAUDIO_OPTION 0x3028 +/* Sets control options for PAD_RETENTION_GPIO */ +#define PAD_RETENTION_GPIO_OPTION 0x3108 +/* Sets control options for PAD_RETENTION_UART */ +#define PAD_RETENTION_UART_OPTION 0x3128 +/* Sets control options for PAD_RETENTION_MMCA */ +#define PAD_RETENTION_MMCA_OPTION 0x3148 +/* Sets control options for PAD_RETENTION_MMCB */ +#define PAD_RETENTION_MMCB_OPTION 0x3168 +/* Sets control options for PAD_RETENTION_EBIA */ +#define PAD_RETENTION_EBIA_OPTION 0x3188 +/* Sets control options for PAD_RETENTION_EBIB */ +#define PAD_RETENTION_EBIB_OPTION 0x31A8 +#define PS_HOLD_CONTROL 0x330C /* PS_HOLD control register */ +#define XUSBXTI_CONFIGURATION 0x3400 /* Configure the pad of XUSBXTI */ +#define XUSBXTI_STATUS 0x3404 /* Check the pad of XUSBXTI */ +/* Sets time required for XUSBXTI to be stabilized */ +#define XUSBXTI_DURATION 0x341C +#define XXTI_CONFIGURATION 0x3420 /* Configure the pad of XXTI */ +#define XXTI_STATUS 0x3424 /* Check the pad of XXTI */ +/* Sets time required for XXTI to be stabilized */ +#define XXTI_DURATION 0x343C +/* Sets time required for EXT_REGULATOR to be stabilized */ +#define EXT_REGULATOR_DURATION 0x361C +#define CAM_CONFIGURATION 0x3C00 /* Configure power mode of CAM */ +#define CAM_STATUS 0x3C04 /* Check power mode of CAM */ +#define CAM_OPTION 0x3C08 /* Sets control options for CAM */ +#define TV_CONFIGURATION 0x3C20 /* Configure power mode of TV */ +#define TV_STATUS 0x3C24 /* Check power mode of TV */ +#define TV_OPTION 0x3C28 /* Sets control options for TV */ +#define MFC_CONFIGURATION 0x3C40 /* Configure power mode of MFC */ +#define MFC_STATUS 0x3C44 /* Check power mode of MFC */ +#define MFC_OPTION 0x3C48 /* Sets control options for MFC */ +#define G3D_CONFIGURATION 0x3C60 /* Configure power mode of G3D */ +#define G3D_STATUS 0x3C64 /* Check power mode of G3D */ +#define G3D_OPTION 0x3C68 /* Sets control options for G3D */ +#define LCD0_CONFIGURATION 0x3C80 /* Configure power mode of LCD0 */ +#define LCD0_STATUS 0x3C84 /* Check power mode of LCD0 */ +#define LCD0_OPTION 0x3C88 /* Sets control options for LCD0 */ +#define LCD1_CONFIGURATION 0x3CA0 /* Configure power mode of LCD1 */ +#define LCD1_STATUS 0x3CA4 /* Check power mode of LCD1 */ +#define LCD1_OPTION 0x3CA8 /* Sets control options for LCD1 */ +#define GPS_CONFIGURATION 0x3CE0 /* Configure power mode of GPS */ +#define GPS_STATUS 0x3CE4 /* Check power mode of GPS */ +#define GPS_OPTION 0x3CE8 /* Sets control options for GPS */ +#define GPS_ALIVE_CONFIGURATION 0x3D00 /* Configure power mode of GPS */ +#define GPS_ALIVE_STATUS 0x3D04 /* Check power mode of GPS */ +#define GPS_ALIVE_OPTION 0x3D08 /* Sets control options for GPS */ + +#define EXYNOS4210_PMU_REGS_MEM_SIZE 0x3d0c + +typedef struct Exynos4210PmuReg { + const char *name; /* for debug only */ + uint32_t offset; + uint32_t reset_value; +} Exynos4210PmuReg; + +static const Exynos4210PmuReg exynos4210_pmu_regs[] = { + {"OM_STAT", OM_STAT, 0x00000000}, + {"RTC_CLKO_SEL", RTC_CLKO_SEL, 0x00000000}, + {"GNSS_RTC_OUT_CTRL", GNSS_RTC_OUT_CTRL, 0x00000001}, + {"SYSTEM_POWER_DOWN_CTRL", SYSTEM_POWER_DOWN_CTRL, 0x00010000}, + {"SYSTEM_POWER_DOWN_OPTION", SYSTEM_POWER_DOWN_OPTION, 0x03030000}, + {"SWRESET", SWRESET, 0x00000000}, + {"RST_STAT", RST_STAT, 0x00000000}, + {"WAKEUP_STAT", WAKEUP_STAT, 0x00000000}, + {"EINT_WAKEUP_MASK", EINT_WAKEUP_MASK, 0x00000000}, + {"WAKEUP_MASK", WAKEUP_MASK, 0x00000000}, + {"HDMI_PHY_CONTROL", HDMI_PHY_CONTROL, 0x00960000}, + {"USBDEVICE_PHY_CONTROL", USBDEVICE_PHY_CONTROL, 0x00000000}, + {"USBHOST_PHY_CONTROL", USBHOST_PHY_CONTROL, 0x00000000}, + {"DAC_PHY_CONTROL", DAC_PHY_CONTROL, 0x00000000}, + {"MIPI_PHY0_CONTROL", MIPI_PHY0_CONTROL, 0x00000000}, + {"MIPI_PHY1_CONTROL", MIPI_PHY1_CONTROL, 0x00000000}, + {"ADC_PHY_CONTROL", ADC_PHY_CONTROL, 0x00000001}, + {"PCIe_PHY_CONTROL", PCIe_PHY_CONTROL, 0x00000000}, + {"SATA_PHY_CONTROL", SATA_PHY_CONTROL, 0x00000000}, + {"INFORM0", INFORM0, 0x00000000}, + {"INFORM1", INFORM1, 0x00000000}, + {"INFORM2", INFORM2, 0x00000000}, + {"INFORM3", INFORM3, 0x00000000}, + {"INFORM4", INFORM4, 0x00000000}, + {"INFORM5", INFORM5, 0x00000000}, + {"INFORM6", INFORM6, 0x00000000}, + {"INFORM7", INFORM7, 0x00000000}, + {"PMU_DEBUG", PMU_DEBUG, 0x00000000}, + {"ARM_CORE0_SYS_PWR_REG", ARM_CORE0_SYS_PWR_REG, 0xFFFFFFFF}, + {"ARM_CORE1_SYS_PWR_REG", ARM_CORE1_SYS_PWR_REG, 0xFFFFFFFF}, + {"ARM_COMMON_SYS_PWR_REG", ARM_COMMON_SYS_PWR_REG, 0xFFFFFFFF}, + {"ARM_CPU_L2_0_SYS_PWR_REG", ARM_CPU_L2_0_SYS_PWR_REG, 0xFFFFFFFF}, + {"ARM_CPU_L2_1_SYS_PWR_REG", ARM_CPU_L2_1_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_ACLKSTOP_SYS_PWR_REG", CMU_ACLKSTOP_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_SCLKSTOP_SYS_PWR_REG", CMU_SCLKSTOP_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_SYS_PWR_REG", CMU_RESET_SYS_PWR_REG, 0xFFFFFFFF}, + {"APLL_SYSCLK_SYS_PWR_REG", APLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, + {"MPLL_SYSCLK_SYS_PWR_REG", MPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, + {"VPLL_SYSCLK_SYS_PWR_REG", VPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, + {"EPLL_SYSCLK_SYS_PWR_REG", EPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG", CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG, + 0xFFFFFFFF}, + {"CMU_RESET_GPS_ALIVE_SYS_PWR_REG", CMU_RESET_GPS_ALIVE_SYS_PWR_REG, + 0xFFFFFFFF}, + {"CMU_CLKSTOP_CAM_SYS_PWR_REG", CMU_CLKSTOP_CAM_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_TV_SYS_PWR_REG", CMU_CLKSTOP_TV_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_MFC_SYS_PWR_REG", CMU_CLKSTOP_MFC_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_G3D_SYS_PWR_REG", CMU_CLKSTOP_G3D_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_LCD0_SYS_PWR_REG", CMU_CLKSTOP_LCD0_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_LCD1_SYS_PWR_REG", CMU_CLKSTOP_LCD1_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_CLKSTOP_MAUDIO_SYS_PWR_REG", CMU_CLKSTOP_MAUDIO_SYS_PWR_REG, + 0xFFFFFFFF}, + {"CMU_CLKSTOP_GPS_SYS_PWR_REG", CMU_CLKSTOP_GPS_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_CAM_SYS_PWR_REG", CMU_RESET_CAM_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_TV_SYS_PWR_REG", CMU_RESET_TV_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_MFC_SYS_PWR_REG", CMU_RESET_MFC_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_G3D_SYS_PWR_REG", CMU_RESET_G3D_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_LCD0_SYS_PWR_REG", CMU_RESET_LCD0_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_LCD1_SYS_PWR_REG", CMU_RESET_LCD1_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_MAUDIO_SYS_PWR_REG", CMU_RESET_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, + {"CMU_RESET_GPS_SYS_PWR_REG", CMU_RESET_GPS_SYS_PWR_REG, 0xFFFFFFFF}, + {"TOP_BUS_SYS_PWR_REG", TOP_BUS_SYS_PWR_REG, 0xFFFFFFFF}, + {"TOP_RETENTION_SYS_PWR_REG", TOP_RETENTION_SYS_PWR_REG, 0xFFFFFFFF}, + {"TOP_PWR_SYS_PWR_REG", TOP_PWR_SYS_PWR_REG, 0xFFFFFFFF}, + {"LOGIC_RESET_SYS_PWR_REG", LOGIC_RESET_SYS_PWR_REG, 0xFFFFFFFF}, + {"OneNANDXL_MEM_SYS_PWR_REG", OneNANDXL_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"MODEMIF_MEM_SYS_PWR_REG", MODEMIF_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"USBDEVICE_MEM_SYS_PWR_REG", USBDEVICE_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"SDMMC_MEM_SYS_PWR_REG", SDMMC_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"CSSYS_MEM_SYS_PWR_REG", CSSYS_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"SECSS_MEM_SYS_PWR_REG", SECSS_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"PCIe_MEM_SYS_PWR_REG", PCIe_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"SATA_MEM_SYS_PWR_REG", SATA_MEM_SYS_PWR_REG, 0xFFFFFFFF}, + {"PAD_RETENTION_DRAM_SYS_PWR_REG", PAD_RETENTION_DRAM_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_MAUDIO_SYS_PWR_REG", PAD_RETENTION_MAUDIO_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_GPIO_SYS_PWR_REG", PAD_RETENTION_GPIO_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_UART_SYS_PWR_REG", PAD_RETENTION_UART_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_MMCA_SYS_PWR_REG", PAD_RETENTION_MMCA_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_MMCB_SYS_PWR_REG", PAD_RETENTION_MMCB_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_EBIA_SYS_PWR_REG", PAD_RETENTION_EBIA_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_RETENTION_EBIB_SYS_PWR_REG", PAD_RETENTION_EBIB_SYS_PWR_REG, + 0xFFFFFFFF}, + {"PAD_ISOLATION_SYS_PWR_REG", PAD_ISOLATION_SYS_PWR_REG, 0xFFFFFFFF}, + {"PAD_ALV_SEL_SYS_PWR_REG", PAD_ALV_SEL_SYS_PWR_REG, 0xFFFFFFFF}, + {"XUSBXTI_SYS_PWR_REG", XUSBXTI_SYS_PWR_REG, 0xFFFFFFFF}, + {"XXTI_SYS_PWR_REG", XXTI_SYS_PWR_REG, 0xFFFFFFFF}, + {"EXT_REGULATOR_SYS_PWR_REG", EXT_REGULATOR_SYS_PWR_REG, 0xFFFFFFFF}, + {"GPIO_MODE_SYS_PWR_REG", GPIO_MODE_SYS_PWR_REG, 0xFFFFFFFF}, + {"GPIO_MODE_MAUDIO_SYS_PWR_REG", GPIO_MODE_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, + {"CAM_SYS_PWR_REG", CAM_SYS_PWR_REG, 0xFFFFFFFF}, + {"TV_SYS_PWR_REG", TV_SYS_PWR_REG, 0xFFFFFFFF}, + {"MFC_SYS_PWR_REG", MFC_SYS_PWR_REG, 0xFFFFFFFF}, + {"G3D_SYS_PWR_REG", G3D_SYS_PWR_REG, 0xFFFFFFFF}, + {"LCD0_SYS_PWR_REG", LCD0_SYS_PWR_REG, 0xFFFFFFFF}, + {"LCD1_SYS_PWR_REG", LCD1_SYS_PWR_REG, 0xFFFFFFFF}, + {"MAUDIO_SYS_PWR_REG", MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, + {"GPS_SYS_PWR_REG", GPS_SYS_PWR_REG, 0xFFFFFFFF}, + {"GPS_ALIVE_SYS_PWR_REG", GPS_ALIVE_SYS_PWR_REG, 0xFFFFFFFF}, + {"ARM_CORE0_CONFIGURATION", ARM_CORE0_CONFIGURATION, 0x00000003}, + {"ARM_CORE0_STATUS", ARM_CORE0_STATUS, 0x00030003}, + {"ARM_CORE0_OPTION", ARM_CORE0_OPTION, 0x01010001}, + {"ARM_CORE1_CONFIGURATION", ARM_CORE1_CONFIGURATION, 0x00000003}, + {"ARM_CORE1_STATUS", ARM_CORE1_STATUS, 0x00030003}, + {"ARM_CORE1_OPTION", ARM_CORE1_OPTION, 0x01010001}, + {"ARM_COMMON_OPTION", ARM_COMMON_OPTION, 0x00000001}, + {"ARM_CPU_L2_0_CONFIGURATION", ARM_CPU_L2_0_CONFIGURATION, 0x00000003}, + {"ARM_CPU_L2_0_STATUS", ARM_CPU_L2_0_STATUS, 0x00000003}, + {"ARM_CPU_L2_1_CONFIGURATION", ARM_CPU_L2_1_CONFIGURATION, 0x00000003}, + {"ARM_CPU_L2_1_STATUS", ARM_CPU_L2_1_STATUS, 0x00000003}, + {"PAD_RETENTION_MAUDIO_OPTION", PAD_RETENTION_MAUDIO_OPTION, 0x00000000}, + {"PAD_RETENTION_GPIO_OPTION", PAD_RETENTION_GPIO_OPTION, 0x00000000}, + {"PAD_RETENTION_UART_OPTION", PAD_RETENTION_UART_OPTION, 0x00000000}, + {"PAD_RETENTION_MMCA_OPTION", PAD_RETENTION_MMCA_OPTION, 0x00000000}, + {"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000}, + {"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000}, + {"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000}, + {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200}, + {"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001}, + {"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001}, + {"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000}, + {"XXTI_CONFIGURATION", XXTI_CONFIGURATION, 0x00000001}, + {"XXTI_STATUS", XXTI_STATUS, 0x00000001}, + {"XXTI_DURATION", XXTI_DURATION, 0xFFF00000}, + {"EXT_REGULATOR_DURATION", EXT_REGULATOR_DURATION, 0xFFF03FFF}, + {"CAM_CONFIGURATION", CAM_CONFIGURATION, 0x00000007}, + {"CAM_STATUS", CAM_STATUS, 0x00060007}, + {"CAM_OPTION", CAM_OPTION, 0x00000001}, + {"TV_CONFIGURATION", TV_CONFIGURATION, 0x00000007}, + {"TV_STATUS", TV_STATUS, 0x00060007}, + {"TV_OPTION", TV_OPTION, 0x00000001}, + {"MFC_CONFIGURATION", MFC_CONFIGURATION, 0x00000007}, + {"MFC_STATUS", MFC_STATUS, 0x00060007}, + {"MFC_OPTION", MFC_OPTION, 0x00000001}, + {"G3D_CONFIGURATION", G3D_CONFIGURATION, 0x00000007}, + {"G3D_STATUS", G3D_STATUS, 0x00060007}, + {"G3D_OPTION", G3D_OPTION, 0x00000001}, + {"LCD0_CONFIGURATION", LCD0_CONFIGURATION, 0x00000007}, + {"LCD0_STATUS", LCD0_STATUS, 0x00060007}, + {"LCD0_OPTION", LCD0_OPTION, 0x00000001}, + {"LCD1_CONFIGURATION", LCD1_CONFIGURATION, 0x00000007}, + {"LCD1_STATUS", LCD1_STATUS, 0x00060007}, + {"LCD1_OPTION", LCD1_OPTION, 0x00000001}, + {"GPS_CONFIGURATION", GPS_CONFIGURATION, 0x00000007}, + {"GPS_STATUS", GPS_STATUS, 0x00060007}, + {"GPS_OPTION", GPS_OPTION, 0x00000001}, + {"GPS_ALIVE_CONFIGURATION", GPS_ALIVE_CONFIGURATION, 0x00000007}, + {"GPS_ALIVE_STATUS", GPS_ALIVE_STATUS, 0x00060007}, + {"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001}, +}; + +#define PMU_NUM_OF_REGISTERS \ + (sizeof(exynos4210_pmu_regs) / sizeof(Exynos4210PmuReg)) + +typedef struct Exynos4210PmuState { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t reg[PMU_NUM_OF_REGISTERS]; +} Exynos4210PmuState; + +static uint64_t exynos4210_pmu_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; + unsigned i; + const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + + for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { + if (reg_p->offset == offset) { + PRINT_DEBUG_EXTEND("%s [0x%04x] -> 0x%04x\n", reg_p->name, + (uint32_t)offset, s->reg[i]); + return s->reg[i]; + } + reg_p++; + } + PRINT_DEBUG("QEMU PMU ERROR: bad read offset 0x%04x\n", (uint32_t)offset); + return 0; +} + +static void exynos4210_pmu_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; + unsigned i; + const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; + + for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { + if (reg_p->offset == offset) { + PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name, + (uint32_t)offset, (uint32_t)val); + s->reg[i] = val; + return; + } + reg_p++; + } + PRINT_DEBUG("QEMU PMU ERROR: bad write offset 0x%04x\n", (uint32_t)offset); +} + +static const MemoryRegionOps exynos4210_pmu_ops = { + .read = exynos4210_pmu_read, + .write = exynos4210_pmu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + } +}; + +static void exynos4210_pmu_reset(DeviceState *dev) +{ + Exynos4210PmuState *s = + container_of(dev, Exynos4210PmuState, busdev.qdev); + unsigned i; + + /* Set default values for registers */ + for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { + s->reg[i] = exynos4210_pmu_regs[i].reset_value; + } +} + +static int exynos4210_pmu_init(SysBusDevice *dev) +{ + Exynos4210PmuState *s = FROM_SYSBUS(Exynos4210PmuState, dev); + + /* memory mapping */ + memory_region_init_io(&s->iomem, &exynos4210_pmu_ops, s, "exynos4210.pmu", + EXYNOS4210_PMU_REGS_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + return 0; +} + +static const VMStateDescription exynos4210_pmu_vmstate = { + .name = "exynos4210.pmu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_pmu_init; + dc->reset = exynos4210_pmu_reset; + dc->vmsd = &exynos4210_pmu_vmstate; +} + +static TypeInfo exynos4210_pmu_info = { + .name = "exynos4210.pmu", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210PmuState), + .class_init = exynos4210_pmu_class_init, +}; + +static void exynos4210_pmu_register(void) +{ + type_register_static(&exynos4210_pmu_info); +} + +type_init(exynos4210_pmu_register) diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c new file mode 100644 index 0000000000..6243e59c48 --- /dev/null +++ b/hw/exynos4210_pwm.c @@ -0,0 +1,422 @@ +/* + * Samsung exynos4210 Pulse Width Modulation Timer + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin <e.voevodin@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "sysbus.h" +#include "qemu-timer.h" +#include "qemu-common.h" +#include "ptimer.h" + +#include "exynos4210.h" + +//#define DEBUG_PWM + +#ifdef DEBUG_PWM +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define EXYNOS4210_PWM_TIMERS_NUM 5 +#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50 + +#define TCFG0 0x0000 +#define TCFG1 0x0004 +#define TCON 0x0008 +#define TCNTB0 0x000C +#define TCMPB0 0x0010 +#define TCNTO0 0x0014 +#define TCNTB1 0x0018 +#define TCMPB1 0x001C +#define TCNTO1 0x0020 +#define TCNTB2 0x0024 +#define TCMPB2 0x0028 +#define TCNTO2 0x002C +#define TCNTB3 0x0030 +#define TCMPB3 0x0034 +#define TCNTO3 0x0038 +#define TCNTB4 0x003C +#define TCNTO4 0x0040 +#define TINT_CSTAT 0x0044 + +#define TCNTB(x) (0xC * (x)) +#define TCMPB(x) (0xC * (x) + 1) +#define TCNTO(x) (0xC * (x) + 2) + +#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x)) +#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x)))) + +/* + * Attention! Timer4 doesn't have OUTPUT_INVERTER, + * so Auto Reload bit is not accessible by macros! + */ +#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x)) +#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0)) +#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1)) +#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2)) +#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3)) +#define TCON_TIMER4_AUTO_RELOAD (1 << 22) + +#define TINT_CSTAT_STATUS(x) (1 << (5 + (x))) +#define TINT_CSTAT_ENABLE(x) (1 << (x)) + +/* timer struct */ +typedef struct { + uint32_t id; /* timer id */ + qemu_irq irq; /* local timer irq */ + uint32_t freq; /* timer frequency */ + + /* use ptimer.c to represent count down timer */ + ptimer_state *ptimer; /* timer */ + + /* registers */ + uint32_t reg_tcntb; /* counter register buffer */ + uint32_t reg_tcmpb; /* compare register buffer */ + + struct Exynos4210PWMState *parent; + +} Exynos4210PWM; + + +typedef struct Exynos4210PWMState { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg_tcfg[2]; + uint32_t reg_tcon; + uint32_t reg_tint_cstat; + + Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM]; + +} Exynos4210PWMState; + +/*** VMState ***/ +static const VMStateDescription vmstate_exynos4210_pwm = { + .name = "exynos4210.pwm.pwm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, Exynos4210PWM), + VMSTATE_UINT32(freq, Exynos4210PWM), + VMSTATE_PTIMER(ptimer, Exynos4210PWM), + VMSTATE_UINT32(reg_tcntb, Exynos4210PWM), + VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_pwm_state = { + .name = "exynos4210.pwm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2), + VMSTATE_UINT32(reg_tcon, Exynos4210PWMState), + VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState), + VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState, + EXYNOS4210_PWM_TIMERS_NUM, 0, + vmstate_exynos4210_pwm, Exynos4210PWM), + VMSTATE_END_OF_LIST() + } +}; + +/* + * PWM update frequency + */ +static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id) +{ + uint32_t freq; + freq = s->timer[id].freq; + if (id > 1) { + s->timer[id].freq = 24000000 / + ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) * + (GET_DIVIDER(s->reg_tcfg[1], id))); + } else { + s->timer[id].freq = 24000000 / + ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) * + (GET_DIVIDER(s->reg_tcfg[1], id))); + } + + if (freq != s->timer[id].freq) { + ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq); + DPRINTF("freq=%dHz\n", s->timer[id].freq); + } +} + +/* + * Counter tick handler + */ +static void exynos4210_pwm_tick(void *opaque) +{ + Exynos4210PWM *s = (Exynos4210PWM *)opaque; + Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent; + uint32_t id = s->id; + bool cmp; + + DPRINTF("timer %d tick\n", id); + + /* set irq status */ + p->reg_tint_cstat |= TINT_CSTAT_STATUS(id); + + /* raise IRQ */ + if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) { + DPRINTF("timer %d IRQ\n", id); + qemu_irq_raise(p->timer[id].irq); + } + + /* reload timer */ + if (id != 4) { + cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id); + } else { + cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD; + } + + if (cmp) { + DPRINTF("auto reload timer %d count to %x\n", id, + p->timer[id].reg_tcntb); + ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb); + ptimer_run(p->timer[id].ptimer, 1); + } else { + /* stop timer, set status to STOP, see Basic Timer Operation */ + p->reg_tcon = ~TCON_TIMER_START(id); + ptimer_stop(p->timer[id].ptimer); + } +} + +/* + * PWM Read + */ +static uint64_t exynos4210_pwm_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; + uint32_t value = 0; + int index; + + switch (offset) { + case TCFG0: case TCFG1: + index = (offset - TCFG0) >> 2; + value = s->reg_tcfg[index]; + break; + + case TCON: + value = s->reg_tcon; + break; + + case TCNTB0: case TCNTB1: + case TCNTB2: case TCNTB3: case TCNTB4: + index = (offset - TCNTB0) / 0xC; + value = s->timer[index].reg_tcntb; + break; + + case TCMPB0: case TCMPB1: + case TCMPB2: case TCMPB3: + index = (offset - TCMPB0) / 0xC; + value = s->timer[index].reg_tcmpb; + break; + + case TCNTO0: case TCNTO1: + case TCNTO2: case TCNTO3: case TCNTO4: + index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC; + value = ptimer_get_count(s->timer[index].ptimer); + break; + + case TINT_CSTAT: + value = s->reg_tint_cstat; + break; + + default: + fprintf(stderr, + "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", + offset); + break; + } + return value; +} + +/* + * PWM Write + */ +static void exynos4210_pwm_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; + int index; + uint32_t new_val; + int i; + + switch (offset) { + case TCFG0: case TCFG1: + index = (offset - TCFG0) >> 2; + s->reg_tcfg[index] = value; + + /* update timers frequencies */ + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + exynos4210_pwm_update_freq(s, s->timer[i].id); + } + break; + + case TCON: + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + if ((value & TCON_TIMER_MANUAL_UPD(i)) > + (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) { + /* + * TCNTB and TCMPB are loaded into TCNT and TCMP. + * Update timers. + */ + + /* this will start timer to run, this ok, because + * during processing start bit timer will be stopped + * if needed */ + ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb); + DPRINTF("set timer %d count to %x\n", i, + s->timer[i].reg_tcntb); + } + + if ((value & TCON_TIMER_START(i)) > + (s->reg_tcon & TCON_TIMER_START(i))) { + /* changed to start */ + ptimer_run(s->timer[i].ptimer, 1); + DPRINTF("run timer %d\n", i); + } + + if ((value & TCON_TIMER_START(i)) < + (s->reg_tcon & TCON_TIMER_START(i))) { + /* changed to stop */ + ptimer_stop(s->timer[i].ptimer); + DPRINTF("stop timer %d\n", i); + } + } + s->reg_tcon = value; + break; + + case TCNTB0: case TCNTB1: + case TCNTB2: case TCNTB3: case TCNTB4: + index = (offset - TCNTB0) / 0xC; + s->timer[index].reg_tcntb = value; + break; + + case TCMPB0: case TCMPB1: + case TCMPB2: case TCMPB3: + index = (offset - TCMPB0) / 0xC; + s->timer[index].reg_tcmpb = value; + break; + + case TINT_CSTAT: + new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value); + new_val &= ~(0x3E0 & value); + + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + if ((new_val & TINT_CSTAT_STATUS(i)) < + (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) { + qemu_irq_lower(s->timer[i].irq); + } + } + + s->reg_tint_cstat = new_val; + break; + + default: + fprintf(stderr, + "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", + offset); + break; + + } +} + +/* + * Set default values to timer fields and registers + */ +static void exynos4210_pwm_reset(DeviceState *d) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)d; + int i; + s->reg_tcfg[0] = 0x0101; + s->reg_tcfg[1] = 0x0; + s->reg_tcon = 0; + s->reg_tint_cstat = 0; + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + s->timer[i].reg_tcmpb = 0; + s->timer[i].reg_tcntb = 0; + + exynos4210_pwm_update_freq(s, s->timer[i].id); + ptimer_stop(s->timer[i].ptimer); + } +} + +static const MemoryRegionOps exynos4210_pwm_ops = { + .read = exynos4210_pwm_read, + .write = exynos4210_pwm_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* + * PWM timer initialization + */ +static int exynos4210_pwm_init(SysBusDevice *dev) +{ + Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev); + int i; + QEMUBH *bh; + + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]); + sysbus_init_irq(dev, &s->timer[i].irq); + s->timer[i].ptimer = ptimer_init(bh); + s->timer[i].id = i; + s->timer[i].parent = s; + } + + memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm", + EXYNOS4210_PWM_REG_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_pwm_init; + dc->reset = exynos4210_pwm_reset; + dc->vmsd = &vmstate_exynos4210_pwm_state; +} + +static TypeInfo exynos4210_pwm_info = { + .name = "exynos4210.pwm", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210PWMState), + .class_init = exynos4210_pwm_class_init, +}; + +static void exynos4210_pwm_register_types(void) +{ + type_register_static(&exynos4210_pwm_info); +} + +type_init(exynos4210_pwm_register_types) diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c new file mode 100644 index 0000000000..73a9c18f30 --- /dev/null +++ b/hw/exynos4210_uart.c @@ -0,0 +1,676 @@ +/* + * Exynos4210 UART Emulation + * + * Copyright (C) 2011 Samsung Electronics Co Ltd. + * Maksim Kozlov, <m.kozlov@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "sysbus.h" +#include "sysemu.h" +#include "qemu-char.h" + +#include "exynos4210.h" + +#undef DEBUG_UART +#undef DEBUG_UART_EXTEND +#undef DEBUG_IRQ +#undef DEBUG_Rx_DATA +#undef DEBUG_Tx_DATA + +#define DEBUG_UART 0 +#define DEBUG_UART_EXTEND 0 +#define DEBUG_IRQ 0 +#define DEBUG_Rx_DATA 0 +#define DEBUG_Tx_DATA 0 + +#if DEBUG_UART +#define PRINT_DEBUG(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) + +#if DEBUG_UART_EXTEND +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) +#else +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do {} while (0) +#endif /* EXTEND */ + +#else +#define PRINT_DEBUG(fmt, args...) \ + do {} while (0) +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do {} while (0) +#endif + +#define PRINT_ERROR(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) + +/* + * Offsets for UART registers relative to SFR base address + * for UARTn + * + */ +#define ULCON 0x0000 /* Line Control */ +#define UCON 0x0004 /* Control */ +#define UFCON 0x0008 /* FIFO Control */ +#define UMCON 0x000C /* Modem Control */ +#define UTRSTAT 0x0010 /* Tx/Rx Status */ +#define UERSTAT 0x0014 /* UART Error Status */ +#define UFSTAT 0x0018 /* FIFO Status */ +#define UMSTAT 0x001C /* Modem Status */ +#define UTXH 0x0020 /* Transmit Buffer */ +#define URXH 0x0024 /* Receive Buffer */ +#define UBRDIV 0x0028 /* Baud Rate Divisor */ +#define UFRACVAL 0x002C /* Divisor Fractional Value */ +#define UINTP 0x0030 /* Interrupt Pending */ +#define UINTSP 0x0034 /* Interrupt Source Pending */ +#define UINTM 0x0038 /* Interrupt Mask */ + +/* + * for indexing register in the uint32_t array + * + * 'reg' - register offset (see offsets definitions above) + * + */ +#define I_(reg) (reg / sizeof(uint32_t)) + +typedef struct Exynos4210UartReg { + const char *name; /* the only reason is the debug output */ + target_phys_addr_t offset; + uint32_t reset_value; +} Exynos4210UartReg; + +static Exynos4210UartReg exynos4210_uart_regs[] = { + {"ULCON", ULCON, 0x00000000}, + {"UCON", UCON, 0x00003000}, + {"UFCON", UFCON, 0x00000000}, + {"UMCON", UMCON, 0x00000000}, + {"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */ + {"UERSTAT", UERSTAT, 0x00000000}, /* RO */ + {"UFSTAT", UFSTAT, 0x00000000}, /* RO */ + {"UMSTAT", UMSTAT, 0x00000000}, /* RO */ + {"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/ + {"URXH", URXH, 0x00000000}, /* RO */ + {"UBRDIV", UBRDIV, 0x00000000}, + {"UFRACVAL", UFRACVAL, 0x00000000}, + {"UINTP", UINTP, 0x00000000}, + {"UINTSP", UINTSP, 0x00000000}, + {"UINTM", UINTM, 0x00000000}, +}; + +#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C + +/* UART FIFO Control */ +#define UFCON_FIFO_ENABLE 0x1 +#define UFCON_Rx_FIFO_RESET 0x2 +#define UFCON_Tx_FIFO_RESET 0x4 +#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8 +#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT) +#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4 +#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) + +/* Uart FIFO Status */ +#define UFSTAT_Rx_FIFO_COUNT 0xff +#define UFSTAT_Rx_FIFO_FULL 0x100 +#define UFSTAT_Rx_FIFO_ERROR 0x200 +#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16 +#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT) +#define UFSTAT_Tx_FIFO_FULL_SHIFT 24 +#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT) + +/* UART Interrupt Source Pending */ +#define UINTSP_RXD 0x1 /* Receive interrupt */ +#define UINTSP_ERROR 0x2 /* Error interrupt */ +#define UINTSP_TXD 0x4 /* Transmit interrupt */ +#define UINTSP_MODEM 0x8 /* Modem interrupt */ + +/* UART Line Control */ +#define ULCON_IR_MODE_SHIFT 6 +#define ULCON_PARITY_SHIFT 3 +#define ULCON_STOP_BIT_SHIFT 1 + +/* UART Tx/Rx Status */ +#define UTRSTAT_TRANSMITTER_EMPTY 0x4 +#define UTRSTAT_Tx_BUFFER_EMPTY 0x2 +#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 + +/* UART Error Status */ +#define UERSTAT_OVERRUN 0x1 +#define UERSTAT_PARITY 0x2 +#define UERSTAT_FRAME 0x4 +#define UERSTAT_BREAK 0x8 + +typedef struct { + uint8_t *data; + uint32_t sp, rp; /* store and retrieve pointers */ + uint32_t size; +} Exynos4210UartFIFO; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)]; + Exynos4210UartFIFO rx; + Exynos4210UartFIFO tx; + + CharDriverState *chr; + qemu_irq irq; + + uint32_t channel; + +} Exynos4210UartState; + + +#if DEBUG_UART +/* Used only for debugging inside PRINT_DEBUG_... macros */ +static const char *exynos4210_uart_regname(target_phys_addr_t offset) +{ + + int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg); + int i; + + for (i = 0; i < regs_number; i++) { + if (offset == exynos4210_uart_regs[i].offset) { + return exynos4210_uart_regs[i].name; + } + } + + return NULL; +} +#endif + + +static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) +{ + q->data[q->sp] = ch; + q->sp = (q->sp + 1) % q->size; +} + +static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) +{ + uint8_t ret = q->data[q->rp]; + q->rp = (q->rp + 1) % q->size; + return ret; +} + +static int fifo_elements_number(Exynos4210UartFIFO *q) +{ + if (q->sp < q->rp) { + return q->size - q->rp + q->sp; + } + + return q->sp - q->rp; +} + +static int fifo_empty_elements_number(Exynos4210UartFIFO *q) +{ + return q->size - fifo_elements_number(q); +} + +static void fifo_reset(Exynos4210UartFIFO *q) +{ + if (q->data != NULL) { + g_free(q->data); + q->data = NULL; + } + + q->data = (uint8_t *)g_malloc0(q->size); + + q->sp = 0; + q->rp = 0; +} + +static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) +{ + uint32_t level = 0; + uint32_t reg; + + reg = (s->reg[I_(UFCON)] && UFCON_Tx_FIFO_TRIGGER_LEVEL) >> + UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; + + switch (s->channel) { + case 0: + level = reg * 32; + break; + case 1: + case 4: + level = reg * 8; + break; + case 2: + case 3: + level = reg * 2; + break; + default: + level = 0; + PRINT_ERROR("Wrong UART channel number: %d\n", s->channel); + } + + return level; +} + +static void exynos4210_uart_update_irq(Exynos4210UartState *s) +{ + /* + * The Tx interrupt is always requested if the number of data in the + * transmit FIFO is smaller than the trigger level. + */ + if (s->reg[I_(UFCON)] && UFCON_FIFO_ENABLE) { + + uint32_t count = (s->reg[I_(UFSTAT)] && UFSTAT_Tx_FIFO_COUNT) >> + UFSTAT_Tx_FIFO_COUNT_SHIFT; + + if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { + s->reg[I_(UINTSP)] |= UINTSP_TXD; + } + } + + s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; + + if (s->reg[I_(UINTP)]) { + qemu_irq_raise(s->irq); + +#if DEBUG_IRQ + fprintf(stderr, "UART%d: IRQ has been raised: %08x\n", + s->channel, s->reg[I_(UINTP)]); +#endif + + } else { + qemu_irq_lower(s->irq); + } +} + +static void exynos4210_uart_update_parameters(Exynos4210UartState *s) +{ + int speed, parity, data_bits, stop_bits, frame_size; + QEMUSerialSetParams ssp; + uint64_t uclk_rate; + + if (s->reg[I_(UBRDIV)] == 0) { + return; + } + + frame_size = 1; /* start bit */ + if (s->reg[I_(ULCON)] & 0x20) { + frame_size++; /* parity bit */ + if (s->reg[I_(ULCON)] & 0x28) { + parity = 'E'; + } else { + parity = 'O'; + } + } else { + parity = 'N'; + } + + if (s->reg[I_(ULCON)] & 0x4) { + stop_bits = 2; + } else { + stop_bits = 1; + } + + data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; + + frame_size += data_bits + stop_bits; + + uclk_rate = 24000000; + + speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + + (s->reg[I_(UFRACVAL)] & 0x7) + 16); + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", + s->channel, speed, parity, data_bits, stop_bits); +} + +static void exynos4210_uart_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + uint8_t ch; + + PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel, + offset, exynos4210_uart_regname(offset), (long long unsigned int)val); + + switch (offset) { + case ULCON: + case UBRDIV: + case UFRACVAL: + s->reg[I_(offset)] = val; + exynos4210_uart_update_parameters(s); + break; + case UFCON: + s->reg[I_(UFCON)] = val; + if (val & UFCON_Rx_FIFO_RESET) { + fifo_reset(&s->rx); + s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; + PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel); + } + if (val & UFCON_Tx_FIFO_RESET) { + fifo_reset(&s->tx); + s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; + PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel); + } + break; + + case UTXH: + if (s->chr) { + s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | + UTRSTAT_Tx_BUFFER_EMPTY); + ch = (uint8_t)val; + qemu_chr_fe_write(s->chr, &ch, 1); +#if DEBUG_Tx_DATA + fprintf(stderr, "%c", ch); +#endif + s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | + UTRSTAT_Tx_BUFFER_EMPTY; + s->reg[I_(UINTSP)] |= UINTSP_TXD; + exynos4210_uart_update_irq(s); + } + break; + + case UINTP: + s->reg[I_(UINTP)] &= ~val; + s->reg[I_(UINTSP)] &= ~val; + PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n", + s->channel, offset, s->reg[I_(UINTP)]); + exynos4210_uart_update_irq(s); + break; + case UTRSTAT: + case UERSTAT: + case UFSTAT: + case UMSTAT: + case URXH: + PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n", + s->channel, exynos4210_uart_regname(offset), offset); + break; + case UINTSP: + s->reg[I_(UINTSP)] &= ~val; + break; + case UINTM: + s->reg[I_(UINTM)] = val; + exynos4210_uart_update_irq(s); + break; + case UCON: + case UMCON: + default: + s->reg[I_(offset)] = val; + break; + } +} +static uint64_t exynos4210_uart_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + uint32_t res; + + switch (offset) { + case UERSTAT: /* Read Only */ + res = s->reg[I_(UERSTAT)]; + s->reg[I_(UERSTAT)] = 0; + return res; + case UFSTAT: /* Read Only */ + s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff; + if (fifo_empty_elements_number(&s->rx) == 0) { + s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; + s->reg[I_(UFSTAT)] &= ~0xff; + } + return s->reg[I_(UFSTAT)]; + case URXH: + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + if (fifo_elements_number(&s->rx)) { + res = fifo_retrieve(&s->rx); +#if DEBUG_Rx_DATA + fprintf(stderr, "%c", res); +#endif + if (!fifo_elements_number(&s->rx)) { + s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; + } else { + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + } else { + s->reg[I_(UINTSP)] |= UINTSP_ERROR; + exynos4210_uart_update_irq(s); + res = 0; + } + } else { + s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; + res = s->reg[I_(URXH)]; + } + return res; + case UTXH: + PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n", + s->channel, exynos4210_uart_regname(offset), offset); + break; + default: + return s->reg[I_(offset)]; + } + + return 0; +} + +static const MemoryRegionOps exynos4210_uart_ops = { + .read = exynos4210_uart_read, + .write = exynos4210_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .unaligned = false + }, +}; + +static int exynos4210_uart_can_receive(void *opaque) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + + return fifo_empty_elements_number(&s->rx); +} + + +static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + int i; + + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + if (fifo_empty_elements_number(&s->rx) < size) { + for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { + fifo_store(&s->rx, buf[i]); + } + s->reg[I_(UINTSP)] |= UINTSP_ERROR; + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } else { + for (i = 0; i < size; i++) { + fifo_store(&s->rx, buf[i]); + } + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + /* XXX: Around here we maybe should check Rx trigger level */ + s->reg[I_(UINTSP)] |= UINTSP_RXD; + } else { + s->reg[I_(URXH)] = buf[0]; + s->reg[I_(UINTSP)] |= UINTSP_RXD; + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + + exynos4210_uart_update_irq(s); +} + + +static void exynos4210_uart_event(void *opaque, int event) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + + if (event == CHR_EVENT_BREAK) { + /* When the RxDn is held in logic 0, then a null byte is pushed into the + * fifo */ + fifo_store(&s->rx, '\0'); + s->reg[I_(UERSTAT)] |= UERSTAT_BREAK; + exynos4210_uart_update_irq(s); + } +} + + +static void exynos4210_uart_reset(DeviceState *dev) +{ + Exynos4210UartState *s = + container_of(dev, Exynos4210UartState, busdev.qdev); + int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg); + int i; + + for (i = 0; i < regs_number; i++) { + s->reg[I_(exynos4210_uart_regs[i].offset)] = + exynos4210_uart_regs[i].reset_value; + } + + fifo_reset(&s->rx); + fifo_reset(&s->tx); + + PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size); +} + +static const VMStateDescription vmstate_exynos4210_uart_fifo = { + .name = "exynos4210.uart.fifo", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sp, Exynos4210UartFIFO), + VMSTATE_UINT32(rp, Exynos4210UartFIFO), + VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_uart = { + .name = "exynos4210.uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(rx, Exynos4210UartState, 1, + vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), + VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, + EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)), + VMSTATE_END_OF_LIST() + } +}; + +DeviceState *exynos4210_uart_create(target_phys_addr_t addr, + int fifo_size, + int channel, + CharDriverState *chr, + qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *bus; + + const char chr_name[] = "serial"; + char label[ARRAY_SIZE(chr_name) + 1]; + + dev = qdev_create(NULL, "exynos4210.uart"); + + if (!chr) { + if (channel >= MAX_SERIAL_PORTS) { + hw_error("Only %d serial ports are supported by QEMU.\n", + MAX_SERIAL_PORTS); + } + chr = serial_hds[channel]; + if (!chr) { + snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); + chr = qemu_chr_new(label, "null", NULL); + if (!(chr)) { + hw_error("Can't assign serial port to UART%d.\n", channel); + } + } + } + + qdev_prop_set_chr(dev, "chardev", chr); + qdev_prop_set_uint32(dev, "channel", channel); + qdev_prop_set_uint32(dev, "rx-size", fifo_size); + qdev_prop_set_uint32(dev, "tx-size", fifo_size); + + bus = sysbus_from_qdev(dev); + qdev_init_nofail(dev); + if (addr != (target_phys_addr_t)-1) { + sysbus_mmio_map(bus, 0, addr); + } + sysbus_connect_irq(bus, 0, irq); + + return dev; +} + +static int exynos4210_uart_init(SysBusDevice *dev) +{ + Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev); + + /* memory mapping */ + memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart", + EXYNOS4210_UART_REGS_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + sysbus_init_irq(dev, &s->irq); + + qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, + exynos4210_uart_receive, exynos4210_uart_event, s); + + return 0; +} + +static Property exynos4210_uart_properties[] = { + DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), + DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), + DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), + DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), + DEFINE_PROP_END_OF_LIST(), +}; + +static void exynos4210_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_uart_init; + dc->reset = exynos4210_uart_reset; + dc->props = exynos4210_uart_properties; + dc->vmsd = &vmstate_exynos4210_uart; +} + +static TypeInfo exynos4210_uart_info = { + .name = "exynos4210.uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210UartState), + .class_init = exynos4210_uart_class_init, +}; + +static void exynos4210_uart_register(void) +{ + type_register_static(&exynos4210_uart_info); +} + +type_init(exynos4210_uart_register) diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c new file mode 100644 index 0000000000..553a02b910 --- /dev/null +++ b/hw/exynos4_boards.c @@ -0,0 +1,177 @@ +/* + * Samsung exynos4 SoC based boards emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * Maksim Kozlov <m.kozlov@samsung.com> + * Evgeny Voevodin <e.voevodin@samsung.com> + * Igor Mitsyanko <i.mitsyanko@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "sysemu.h" +#include "sysbus.h" +#include "net.h" +#include "arm-misc.h" +#include "exec-memory.h" +#include "exynos4210.h" +#include "boards.h" + +#undef DEBUG + +//#define DEBUG + +#ifdef DEBUG + #undef PRINT_DEBUG + #define PRINT_DEBUG(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) +#else + #define PRINT_DEBUG(fmt, args...) do {} while (0) +#endif + +#define SMDK_LAN9118_BASE_ADDR 0x05000000 + +typedef enum Exynos4BoardType { + EXYNOS4_BOARD_NURI, + EXYNOS4_BOARD_SMDKC210, + EXYNOS4_NUM_OF_BOARDS +} Exynos4BoardType; + +static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = { + [EXYNOS4_BOARD_NURI] = 0xD33, + [EXYNOS4_BOARD_SMDKC210] = 0xB16, +}; + +static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = { + [EXYNOS4_BOARD_NURI] = EXYNOS4210_SECOND_CPU_BOOTREG, + [EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG, +}; + +static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = { + [EXYNOS4_BOARD_NURI] = 0x40000000, + [EXYNOS4_BOARD_SMDKC210] = 0x40000000, +}; + +static struct arm_boot_info exynos4_board_binfo = { + .loader_start = EXYNOS4210_BASE_BOOT_ADDR, + .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, + .nb_cpus = EXYNOS4210_NCPUS, +}; + +static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS]; + +static void lan9215_init(uint32_t base, qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *s; + + /* This should be a 9215 but the 9118 is close enough */ + if (nd_table[0].vlan) { + qemu_check_nic_model(&nd_table[0], "lan9118"); + dev = qdev_create(NULL, "lan9118"); + qdev_set_nic_properties(dev, &nd_table[0]); + qdev_prop_set_uint32(dev, "mode_16bit", 1); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, irq); + } +} + +static Exynos4210State *exynos4_boards_init_common( + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + Exynos4BoardType board_type) +{ + if (smp_cpus != EXYNOS4210_NCPUS) { + fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus" + " value.\n", + exynos4_machines[board_type].name, + exynos4_machines[board_type].max_cpus); + } + + exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; + exynos4_board_binfo.board_id = exynos4_board_id[board_type]; + exynos4_board_binfo.smp_bootreg_addr = + exynos4_board_smp_bootreg_addr[board_type]; + exynos4_board_binfo.kernel_filename = kernel_filename; + exynos4_board_binfo.initrd_filename = initrd_filename; + exynos4_board_binfo.kernel_cmdline = kernel_cmdline; + exynos4_board_binfo.gic_cpu_if_addr = + EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; + + PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n" + " kernel_filename: %s\n" + " kernel_cmdline: %s\n" + " initrd_filename: %s\n", + exynos4_board_ram_size[board_type] / 1048576, + exynos4_board_ram_size[board_type], + kernel_filename, + kernel_cmdline, + initrd_filename); + + return exynos4210_init(get_system_memory(), + exynos4_board_ram_size[board_type]); +} + +static void nuri_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + exynos4_boards_init_common(kernel_filename, kernel_cmdline, + initrd_filename, EXYNOS4_BOARD_NURI); + + arm_load_kernel(first_cpu, &exynos4_board_binfo); +} + +static void smdkc210_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + Exynos4210State *s = exynos4_boards_init_common(kernel_filename, + kernel_cmdline, initrd_filename, EXYNOS4_BOARD_SMDKC210); + + lan9215_init(SMDK_LAN9118_BASE_ADDR, + qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)])); + arm_load_kernel(first_cpu, &exynos4_board_binfo); +} + +static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = { + [EXYNOS4_BOARD_NURI] = { + .name = "nuri", + .desc = "Samsung NURI board (Exynos4210)", + .init = nuri_init, + .max_cpus = EXYNOS4210_NCPUS, + }, + [EXYNOS4_BOARD_SMDKC210] = { + .name = "smdkc210", + .desc = "Samsung SMDKC210 board (Exynos4210)", + .init = smdkc210_init, + .max_cpus = EXYNOS4210_NCPUS, + }, +}; + +static void exynos4_machine_init(void) +{ + qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]); + qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]); +} + +machine_init(exynos4_machine_init); @@ -62,12 +62,15 @@ #define FD_SECTOR_SC 2 /* Sector size code */ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ +typedef struct FDCtrl FDCtrl; + /* Floppy disk drive emulation */ typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, } FDiskFlags; typedef struct FDrive { + FDCtrl *fdctrl; BlockDriverState *bs; /* Drive status */ FDriveType drive; @@ -83,6 +86,7 @@ typedef struct FDrive { uint16_t bps; /* Bytes per sector */ uint8_t ro; /* Is read-only */ uint8_t media_changed; /* Is media changed */ + uint8_t media_rate; /* Data rate of medium */ } FDrive; static void fd_init(FDrive *drv) @@ -95,16 +99,19 @@ static void fd_init(FDrive *drv) drv->max_track = 0; } +#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) + static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, - uint8_t last_sect) + uint8_t last_sect, uint8_t num_sides) { - return (((track * 2) + head) * last_sect) + sect - 1; + return (((track * num_sides) + head) * last_sect) + sect - 1; } /* Returns current position, in sectors, for given drive */ static int fd_sector(FDrive *drv) { - return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect); + return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect, + NUM_SIDES(drv)); } /* Seek to a new position: @@ -135,7 +142,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, drv->max_track, drv->last_sect); return 3; } - sector = fd_sector_calc(head, track, sect, drv->last_sect); + sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv)); ret = 0; if (sector != fd_sector(drv)) { #if 0 @@ -169,12 +176,13 @@ static void fd_revalidate(FDrive *drv) { int nb_heads, max_track, last_sect, ro; FDriveType drive; + FDriveRate rate; FLOPPY_DPRINTF("revalidate\n"); if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track, - &last_sect, drv->drive, &drive); + &last_sect, drv->drive, &drive, &rate); if (nb_heads != 0 && max_track != 0 && last_sect != 0) { FLOPPY_DPRINTF("User defined disk (%d %d %d)", nb_heads - 1, max_track, last_sect); @@ -191,6 +199,7 @@ static void fd_revalidate(FDrive *drv) drv->last_sect = last_sect; drv->ro = ro; drv->drive = drive; + drv->media_rate = rate; } else { FLOPPY_DPRINTF("No disk in drive\n"); drv->last_sect = 0; @@ -202,13 +211,12 @@ static void fd_revalidate(FDrive *drv) /********************************************************/ /* Intel 82078 floppy disk controller emulation */ -typedef struct FDCtrl FDCtrl; - static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); static void fdctrl_reset_fifo(FDCtrl *fdctrl); static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len); static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0); +static FDrive *get_cur_drv(FDCtrl *fdctrl); static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl); static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl); @@ -221,6 +229,7 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value); static uint32_t fdctrl_read_data(FDCtrl *fdctrl); static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value); static uint32_t fdctrl_read_dir(FDCtrl *fdctrl); +static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value); enum { FD_DIR_WRITE = 0, @@ -245,6 +254,7 @@ enum { FD_REG_DSR = 0x04, FD_REG_FIFO = 0x05, FD_REG_DIR = 0x07, + FD_REG_CCR = 0x07, }; enum { @@ -297,6 +307,8 @@ enum { }; enum { + FD_SR1_MA = 0x01, /* Missing address mark */ + FD_SR1_NW = 0x02, /* Not writable */ FD_SR1_EC = 0x80, /* End of cylinder */ }; @@ -413,6 +425,7 @@ struct FDCtrl { int sun4m; FDrive drives[MAX_FD]; int reset_sensei; + uint32_t check_media_rate; /* Timers state */ uint8_t timer0; uint8_t timer1; @@ -487,6 +500,9 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) case FD_REG_FIFO: fdctrl_write_data(fdctrl, value); break; + case FD_REG_CCR: + fdctrl_write_ccr(fdctrl, value); + break; default: break; } @@ -538,6 +554,24 @@ static const VMStateDescription vmstate_fdrive_media_changed = { } }; +static bool fdrive_media_rate_needed(void *opaque) +{ + FDrive *drive = opaque; + + return drive->fdctrl->check_media_rate; +} + +static const VMStateDescription vmstate_fdrive_media_rate = { + .name = "fdrive/media_rate", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(media_rate, FDrive), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_fdrive = { .name = "fdrive", .version_id = 1, @@ -554,6 +588,9 @@ static const VMStateDescription vmstate_fdrive = { .vmsd = &vmstate_fdrive_media_changed, .needed = &fdrive_media_changed_needed, } , { + .vmsd = &vmstate_fdrive_media_rate, + .needed = &fdrive_media_rate_needed, + } , { /* empty */ } } @@ -877,6 +914,23 @@ static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value) fdctrl->dsr = value; } +/* Configuration control register: 0x07 (write) */ +static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value) +{ + /* Reset mode */ + if (!(fdctrl->dor & FD_DOR_nRESET)) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value); + + /* Only the rate selection bits used in AT mode, and we + * store those in the DSR. + */ + fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) | + (value & FD_DSR_DRATEMASK); +} + static int fdctrl_media_changed(FDrive *drv) { int ret; @@ -903,14 +957,9 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) { uint32_t retval = 0; - if (fdctrl_media_changed(drv0(fdctrl)) - || fdctrl_media_changed(drv1(fdctrl)) -#if MAX_FD == 4 - || fdctrl_media_changed(drv2(fdctrl)) - || fdctrl_media_changed(drv3(fdctrl)) -#endif - ) + if (fdctrl_media_changed(get_cur_drv(fdctrl))) { retval |= FD_DIR_DSKCHG; + } if (retval != 0) { FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); } @@ -1019,7 +1068,8 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) ks = fdctrl->fifo[4]; FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect, + NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1049,6 +1099,19 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) break; } + /* Check the data rate. If the programmed data rate does not match + * the currently inserted medium, the operation has to fail. */ + if (fdctrl->check_media_rate && + (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { + FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", + fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + } + /* Set the FIFO state */ fdctrl->data_dir = direction; fdctrl->data_pos = 0; @@ -1175,6 +1238,16 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, break; case FD_DIR_WRITE: /* WRITE commands */ + if (cur_drv->ro) { + /* Handle readonly medium early, no need to do DMA, touch the + * LED or attempt any writes. A real floppy doesn't attempt + * to write to readonly media either. */ + fdctrl_stop_transfer(fdctrl, + FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, + 0x00); + goto transfer_error; + } + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, fdctrl->data_pos, len); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), @@ -1289,7 +1362,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) ks = fdctrl->fifo[8]; FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect, + NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1343,7 +1417,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) { fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; fdctrl->fifo[0] = fdctrl->lock << 4; - fdctrl_set_fifo(fdctrl, 1, fdctrl->lock); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) @@ -1375,7 +1449,7 @@ static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) { /* Controller's version */ fdctrl->fifo[0] = fdctrl->version; - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) @@ -1434,14 +1508,13 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) fdctrl->fifo[12] = fdctrl->pwrd; fdctrl->fifo[13] = 0; fdctrl->fifo[14] = 0; - fdctrl_set_fifo(fdctrl, 15, 1); + fdctrl_set_fifo(fdctrl, 15, 0); } static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) { FDrive *cur_drv = get_cur_drv(fdctrl); - /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; qemu_mod_timer(fdctrl->result_timer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50)); @@ -1545,13 +1618,16 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); fdctrl_reset_fifo(fdctrl); + /* The seek command just sends step pulses to the drive and doesn't care if + * there is a medium inserted of if it's banging the head against the drive. + */ if (fdctrl->fifo[2] > cur_drv->max_track) { - fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK); + cur_drv->track = cur_drv->max_track; } else { cur_drv->track = fdctrl->fifo[2]; - /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) @@ -1576,7 +1652,7 @@ static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) { fdctrl->pwrd = fdctrl->fifo[1]; fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) @@ -1595,7 +1671,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct fdctrl->fifo[0] = fdctrl->fifo[1]; fdctrl->fifo[2] = 0; fdctrl->fifo[3] = 0; - fdctrl_set_fifo(fdctrl, 4, 1); + fdctrl_set_fifo(fdctrl, 4, 0); } else { fdctrl_reset_fifo(fdctrl); } @@ -1603,7 +1679,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct /* ERROR */ fdctrl->fifo[0] = 0x80 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); - fdctrl_set_fifo(fdctrl, 1, 1); + fdctrl_set_fifo(fdctrl, 1, 0); } } @@ -1729,6 +1805,7 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) pos = command_to_handler[value & 0xff]; FLOPPY_DPRINTF("%s command\n", handlers[pos].name); fdctrl->data_len = handlers[pos].parameters + 1; + fdctrl->msr |= FD_MSR_CMDBUSY; } FLOPPY_DPRINTF("%s: %02x\n", __func__, value); @@ -1760,7 +1837,15 @@ static void fdctrl_result_timer(void *opaque) if (cur_drv->last_sect != 0) { cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; } - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + /* READ_ID can't automatically succeed! */ + if (fdctrl->check_media_rate && + (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { + FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n", + fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + } else { + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + } } static void fdctrl_change_cb(void *opaque, bool load) @@ -1782,6 +1867,7 @@ static int fdctrl_connect_drives(FDCtrl *fdctrl) for (i = 0; i < MAX_FD; i++) { drive = &fdctrl->drives[i]; + drive->fdctrl = fdctrl; if (drive->bs) { if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { @@ -1964,6 +2050,8 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs), DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1), DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1), + DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, + 0, true), DEFINE_PROP_END_OF_LIST(), }; @@ -2043,11 +2131,11 @@ static TypeInfo sun4m_fdc_info = { .class_init = sun4m_fdc_class_init, }; -static void fdc_register_devices(void) +static void fdc_register_types(void) { type_register_static(&isa_fdc_info); type_register_static(&sysbus_fdc_info); type_register_static(&sun4m_fdc_info); } -device_init(fdc_register_devices) +type_init(fdc_register_types) diff --git a/hw/fmopl.c b/hw/fmopl.c index 734d2f4aae..f0a023477d 100644 --- a/hw/fmopl.c +++ b/hw/fmopl.c @@ -733,7 +733,7 @@ INLINE void CSMKeyControll(OPL_CH *CH) } /* ---------- opl initialize ---------- */ -static void OPL_initalize(FM_OPL *OPL) +static void OPL_initialize(FM_OPL *OPL) { int fn; @@ -1239,7 +1239,7 @@ FM_OPL *OPLCreate(int type, int clock, int rate) OPL->rate = rate; OPL->max_ch = max_ch; /* init grobal tables */ - OPL_initalize(OPL); + OPL_initialize(OPL); /* reset chip */ OPLResetChip(OPL); #ifdef OPL_OUTPUT_LOG diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 6b2f7d1986..7b3b5769a2 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -556,9 +556,9 @@ static TypeInfo fw_cfg_info = { .class_init = fw_cfg_class_init, }; -static void fw_cfg_register_devices(void) +static void fw_cfg_register_types(void) { type_register_static(&fw_cfg_info); } -device_init(fw_cfg_register_devices) +type_init(fw_cfg_register_types) diff --git a/hw/g364fb.c b/hw/g364fb.c index 66d0044c06..3a0b68fbae 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; @@ -574,9 +574,9 @@ static TypeInfo g364fb_sysbus_info = { .class_init = g364fb_sysbus_class_init, }; -static void g364fb_register(void) +static void g364fb_register_types(void) { type_register_static(&g364fb_sysbus_info); } -device_init(g364fb_register); +type_init(g364fb_register_types) diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index 8122baf482..81ff3a339a 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -157,10 +157,10 @@ static TypeInfo grackle_pci_host_info = { .class_init = pci_grackle_class_init, }; -static void grackle_register_devices(void) +static void grackle_register_types(void) { type_register_static(&grackle_pci_info); type_register_static(&grackle_pci_host_info); } -device_init(grackle_register_devices) +type_init(grackle_register_types) diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c index 89de2d85ae..73fc9894db 100644 --- a/hw/grlib_apbuart.c +++ b/hw/grlib_apbuart.c @@ -263,9 +263,9 @@ static TypeInfo grlib_gptimer_info = { .class_init = grlib_gptimer_class_init, }; -static void grlib_gptimer_register(void) +static void grlib_gptimer_register_types(void) { type_register_static(&grlib_gptimer_info); } -device_init(grlib_gptimer_register) +type_init(grlib_gptimer_register_types) diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c index fb0b236746..41770a9e6c 100644 --- a/hw/grlib_gptimer.c +++ b/hw/grlib_gptimer.c @@ -396,9 +396,9 @@ static TypeInfo grlib_gptimer_info = { .class_init = grlib_gptimer_class_init, }; -static void grlib_gptimer_register(void) +static void grlib_gptimer_register_types(void) { type_register_static(&grlib_gptimer_info); } -device_init(grlib_gptimer_register) +type_init(grlib_gptimer_register_types) diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c index 1e5ad826f5..0f6e65cf20 100644 --- a/hw/grlib_irqmp.c +++ b/hw/grlib_irqmp.c @@ -377,9 +377,9 @@ static TypeInfo grlib_irqmp_info = { .class_init = grlib_irqmp_class_init, }; -static void grlib_irqmp_register(void) +static void grlib_irqmp_register_types(void) { type_register_static(&grlib_irqmp_info); } -device_init(grlib_irqmp_register) +type_init(grlib_irqmp_register_types) diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index e8cd59c3a4..a2d0e5a2c3 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -1168,10 +1168,10 @@ static TypeInfo gt64120_info = { .class_init = gt64120_class_init, }; -static void gt64120_pci_register_devices(void) +static void gt64120_pci_register_types(void) { type_register_static(>64120_info); type_register_static(>64120_pci_info); } -device_init(gt64120_pci_register_devices) +type_init(gt64120_pci_register_types) @@ -324,8 +324,9 @@ static TypeInfo gus_info = { .class_init = gus_class_initfn, }; -static void gus_register (void) +static void gus_register_types (void) { type_register_static (&gus_info); } -device_init (gus_register) + +type_init (gus_register_types) diff --git a/hw/hda-audio.c b/hw/hda-audio.c index 152f8e6f13..8995519573 100644 --- a/hw/hda-audio.c +++ b/hw/hda-audio.c @@ -948,9 +948,10 @@ static TypeInfo hda_audio_duplex_info = { .class_init = hda_audio_duplex_class_init, }; -static void hda_audio_register(void) +static void hda_audio_register_types(void) { type_register_static(&hda_audio_output_info); type_register_static(&hda_audio_duplex_info); } -device_init(hda_audio_register); + +type_init(hda_audio_register_types) diff --git a/hw/highbank.c b/hw/highbank.c index 684178ee3e..489c00e5b9 100644 --- a/hw/highbank.c +++ b/hw/highbank.c @@ -19,7 +19,6 @@ #include "sysbus.h" #include "arm-misc.h" -#include "primecell.h" #include "devices.h" #include "loader.h" #include "net.h" @@ -177,12 +176,12 @@ static TypeInfo highbank_regs_info = { .class_init = highbank_regs_class_init, }; -static void highbank_regs_register_device(void) +static void highbank_regs_register_types(void) { type_register_static(&highbank_regs_info); } -device_init(highbank_regs_register_device) +type_init(highbank_regs_register_types) static struct arm_boot_info highbank_binfo; @@ -31,6 +31,7 @@ #include "hpet_emul.h" #include "sysbus.h" #include "mc146818rtc.h" +#include "i8254.h" //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -64,6 +65,7 @@ typedef struct HPETState { qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; uint8_t rtc_irq_level; + qemu_irq pit_enabled; uint8_t num_timers; HPETTimer timer[HPET_MAX_TIMERS]; @@ -240,6 +242,24 @@ static int hpet_post_load(void *opaque, int version_id) return 0; } +static bool hpet_rtc_irq_level_needed(void *opaque) +{ + HPETState *s = opaque; + + return s->rtc_irq_level != 0; +} + +static const VMStateDescription vmstate_hpet_rtc_irq_level = { + .name = "hpet/rtc_irq_level", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(rtc_irq_level, HPETState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_hpet_timer = { .name = "hpet_timer", .version_id = 1, @@ -273,6 +293,14 @@ static const VMStateDescription vmstate_hpet = { VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_hpet_rtc_irq_level, + .needed = hpet_rtc_irq_level_needed, + }, { + /* empty */ + } } }; @@ -546,12 +574,15 @@ static void hpet_ram_write(void *opaque, target_phys_addr_t addr, hpet_del_timer(&s->timer[i]); } } - /* i8254 and RTC are disabled when HPET is in legacy mode */ + /* i8254 and RTC output pins are disabled + * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_disable(); + qemu_set_irq(s->pit_enabled, 0); + qemu_irq_lower(s->irqs[0]); qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_enable(); + qemu_irq_lower(s->irqs[0]); + qemu_set_irq(s->pit_enabled, 1); qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); } break; @@ -605,7 +636,6 @@ static void hpet_reset(DeviceState *d) { HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d)); int i; - static int count = 0; for (i = 0; i < s->num_timers; i++) { HPETTimer *timer = &s->timer[i]; @@ -622,29 +652,30 @@ static void hpet_reset(DeviceState *d) timer->wrap_flag = 0; } + qemu_set_irq(s->pit_enabled, 1); s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; s->config = 0ULL; - if (count > 0) { - /* we don't enable pit when hpet_reset is first called (by hpet_init) - * because hpet is taking over for pit here. On subsequent invocations, - * hpet_reset is called due to system reset. At this point control must - * be returned to pit until SW reenables hpet. - */ - hpet_pit_enable(); - } hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr; - count = 1; + + /* to document that the RTC lowers its output on reset as well */ + s->rtc_irq_level = 0; } -static void hpet_handle_rtc_irq(void *opaque, int n, int level) +static void hpet_handle_legacy_irq(void *opaque, int n, int level) { HPETState *s = FROM_SYSBUS(HPETState, opaque); - s->rtc_irq_level = level; - if (!hpet_in_legacy_mode(s)) { - qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); + if (n == HPET_LEGACY_PIT_INT) { + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[0], level); + } + } else { + s->rtc_irq_level = level; + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); + } } } @@ -687,7 +718,8 @@ static int hpet_init(SysBusDevice *dev) s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; s->capability |= ((HPET_CLK_PERIOD) << 32); - qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1); + qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2); + qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1); /* HPET Area */ memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400); @@ -720,9 +752,9 @@ static TypeInfo hpet_device_info = { .class_init = hpet_device_class_init, }; -static void hpet_register_device(void) +static void hpet_register_types(void) { type_register_static(&hpet_device_info); } -device_init(hpet_register_device) +type_init(hpet_register_types) diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h index 6128702533..757f79fdd2 100644 --- a/hw/hpet_emul.h +++ b/hw/hpet_emul.h @@ -22,6 +22,9 @@ #define HPET_NUM_IRQ_ROUTES 32 +#define HPET_LEGACY_PIT_INT 0 +#define HPET_LEGACY_RTC_INT 1 + #define HPET_CFG_ENABLE 0x001 #define HPET_CFG_LEGACY 0x002 @@ -230,9 +230,9 @@ static TypeInfo i2c_slave_type_info = { .class_init = i2c_slave_class_init, }; -static void i2c_slave_register_devices(void) +static void i2c_slave_register_types(void) { type_register_static(&i2c_slave_type_info); } -device_init(i2c_slave_register_devices); +type_init(i2c_slave_register_types) diff --git a/hw/i82374.c b/hw/i82374.c index 220e8cccb6..67298a39f5 100644 --- a/hw/i82374.c +++ b/hw/i82374.c @@ -157,9 +157,9 @@ static TypeInfo i82374_isa_info = { .class_init = i82374_class_init, }; -static void i82374_register_devices(void) +static void i82374_register_types(void) { type_register_static(&i82374_isa_info); } -device_init(i82374_register_devices) +type_init(i82374_register_types) diff --git a/hw/i82378.c b/hw/i82378.c index 9c3efe8b95..faad1a365b 100644 --- a/hw/i82378.c +++ b/hw/i82378.c @@ -19,6 +19,8 @@ #include "pci.h" #include "pc.h" +#include "i8254.h" +#include "pcspk.h" //#define DEBUG_I82378 @@ -191,10 +193,10 @@ static void i82378_init(DeviceState *dev, I82378State *s) isa_bus_irqs(isabus, s->i8259); /* 1 82C54 (pit) */ - pit = pit_init(isabus, 0x40, 0); + pit = pit_init(isabus, 0x40, 0, NULL); /* speaker */ - pcspk_init(pit); + pcspk_init(isabus, pit); /* 2 82C37 (dma) */ DMA_init(1, &s->out[1]); @@ -267,9 +269,9 @@ static TypeInfo pci_i82378_info = { .class_init = pci_i82378_class_init, }; -static void i82378_register_devices(void) +static void i82378_register_types(void) { type_register_static(&pci_i82378_info); } -device_init(i82378_register_devices) +type_init(i82378_register_types) diff --git a/hw/i8254.c b/hw/i8254.c index 522fed8809..f30396af88 100644 --- a/hw/i8254.c +++ b/hw/i8254.c @@ -25,6 +25,7 @@ #include "pc.h" #include "isa.h" #include "qemu-timer.h" +#include "i8254.h" //#define DEBUG_PIT @@ -51,18 +52,16 @@ typedef struct PITChannelState { int64_t next_transition_time; QEMUTimer *irq_timer; qemu_irq irq; + uint32_t irq_disabled; } PITChannelState; typedef struct PITState { ISADevice dev; MemoryRegion ioports; - uint32_t irq; uint32_t iobase; PITChannelState channels[3]; } PITState; -static PITState pit_state; - static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); static int pit_get_count(PITChannelState *s) @@ -91,7 +90,7 @@ static int pit_get_count(PITChannelState *s) } /* get pit output bit */ -static int pit_get_out1(PITChannelState *s, int64_t current_time) +static int pit_get_out(PITChannelState *s, int64_t current_time) { uint64_t d; int out; @@ -123,13 +122,6 @@ static int pit_get_out1(PITChannelState *s, int64_t current_time) return out; } -int pit_get_out(ISADevice *dev, int channel, int64_t current_time) -{ - PITState *pit = DO_UPCAST(PITState, dev, dev); - PITChannelState *s = &pit->channels[channel]; - return pit_get_out1(s, current_time); -} - /* return -1 if no transition will occur. */ static int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time) @@ -216,25 +208,15 @@ void pit_set_gate(ISADevice *dev, int channel, int val) s->gate = val; } -int pit_get_gate(ISADevice *dev, int channel) +void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info) { PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; - return s->gate; -} -int pit_get_initial_count(ISADevice *dev, int channel) -{ - PITState *pit = DO_UPCAST(PITState, dev, dev); - PITChannelState *s = &pit->channels[channel]; - return s->count; -} - -int pit_get_mode(ISADevice *dev, int channel) -{ - PITState *pit = DO_UPCAST(PITState, dev, dev); - PITChannelState *s = &pit->channels[channel]; - return s->mode; + info->gate = s->gate; + info->mode = s->mode; + info->initial_count = s->count; + info->out = pit_get_out(s, qemu_get_clock_ns(vm_clock)); } static inline void pit_load_count(PITChannelState *s, int val) @@ -275,7 +257,9 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (!(val & 0x10) && !s->status_latched) { /* status latch */ /* XXX: add BCD and null count */ - s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) | + s->status = + (pit_get_out(s, + qemu_get_clock_ns(vm_clock)) << 7) | (s->rw_mode << 4) | (s->mode << 1) | s->bcd; @@ -378,10 +362,11 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) int64_t expire_time; int irq_level; - if (!s->irq_timer) + if (!s->irq_timer || s->irq_disabled) { return; + } expire_time = pit_get_next_transition_time(s, current_time); - irq_level = pit_get_out1(s, current_time); + irq_level = pit_get_out(s, current_time); qemu_set_irq(s->irq, irq_level); #ifdef DEBUG_PIT printf("irq_level=%d next_delay=%f\n", @@ -450,6 +435,7 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_8s(f, &s->bcd); qemu_get_8s(f, &s->gate); s->count_load_time=qemu_get_be64(f); + s->irq_disabled = 0; if (s->irq_timer) { s->next_transition_time=qemu_get_be64(f); qemu_get_timer(f, s->irq_timer); @@ -460,11 +446,12 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) static const VMStateDescription vmstate_pit = { .name = "i8254", - .version_id = 2, + .version_id = 3, .minimum_version_id = 2, .minimum_version_id_old = 1, .load_state_old = pit_load_old, .fields = (VMStateField []) { + VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3), VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState), VMSTATE_TIMER(channels[0].irq_timer, PITState), VMSTATE_END_OF_LIST() @@ -481,30 +468,30 @@ static void pit_reset(DeviceState *dev) s = &pit->channels[i]; s->mode = 3; s->gate = (i != 2); - pit_load_count(s, 0); + s->count_load_time = qemu_get_clock_ns(vm_clock); + s->count = 0x10000; + if (i == 0 && !s->irq_disabled) { + s->next_transition_time = + pit_get_next_transition_time(s, s->count_load_time); + qemu_mod_timer(s->irq_timer, s->next_transition_time); + } } } -/* When HPET is operating in legacy mode, i8254 timer0 is disabled */ -void hpet_pit_disable(void) { - PITChannelState *s; - s = &pit_state.channels[0]; - if (s->irq_timer) - qemu_del_timer(s->irq_timer); -} - -/* When HPET is reset or leaving legacy mode, it must reenable i8254 - * timer 0 - */ - -void hpet_pit_enable(void) +/* When HPET is operating in legacy mode, suppress the ignored timer IRQ, + * reenable it when legacy mode is left again. */ +static void pit_irq_control(void *opaque, int n, int enable) { - PITState *pit = &pit_state; - PITChannelState *s; - s = &pit->channels[0]; - s->mode = 3; - s->gate = 1; - pit_load_count(s, 0); + PITState *pit = opaque; + PITChannelState *s = &pit->channels[0]; + + if (enable) { + s->irq_disabled = 0; + pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock)); + } else { + s->irq_disabled = 1; + qemu_del_timer(s->irq_timer); + } } static const MemoryRegionPortio pit_portio[] = { @@ -525,18 +512,19 @@ static int pit_initfn(ISADevice *dev) s = &pit->channels[0]; /* the timer 0 is connected to an IRQ */ s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s); - s->irq = isa_get_irq(dev, pit->irq); + qdev_init_gpio_out(&dev->qdev, &s->irq, 1); memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4); isa_register_ioport(dev, &pit->ioports, pit->iobase); + qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1); + qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2); return 0; } static Property pit_properties[] = { - DEFINE_PROP_UINT32("irq", PITState, irq, -1), DEFINE_PROP_HEX32("iobase", PITState, iobase, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -559,8 +547,9 @@ static TypeInfo pit_info = { .class_init = pit_class_initfn, }; -static void pit_register(void) +static void pit_register_types(void) { type_register_static(&pit_info); } -device_init(pit_register) + +type_init(pit_register_types) diff --git a/hw/i8254.h b/hw/i8254.h new file mode 100644 index 0000000000..a1d2e9835b --- /dev/null +++ b/hw/i8254.h @@ -0,0 +1,57 @@ +/* + * QEMU 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_I8254_H +#define HW_I8254_H + +#include "hw.h" +#include "isa.h" + +#define PIT_FREQ 1193182 + +typedef struct PITChannelInfo { + int gate; + int mode; + int initial_count; + int out; +} PITChannelInfo; + +static inline ISADevice *pit_init(ISABus *bus, int base, int isa_irq, + qemu_irq alt_irq) +{ + ISADevice *dev; + + dev = isa_create(bus, "isa-pit"); + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_init_nofail(&dev->qdev); + qdev_connect_gpio_out(&dev->qdev, 0, + isa_irq >= 0 ? isa_get_irq(dev, isa_irq) : alt_irq); + + return dev; +} + +void pit_set_gate(ISADevice *dev, int channel, int val); +void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info); + +#endif /* !HW_I8254_H */ diff --git a/hw/i8259.c b/hw/i8259.c index 7ae53805d7..53daf78652 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -231,8 +231,8 @@ static void pic_reset(DeviceState *dev) { PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev); - pic_init_reset(s); s->elcr = 0; + pic_init_reset(s); } static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, @@ -488,9 +488,9 @@ static TypeInfo i8259_info = { .class_init = i8259_class_init, }; -static void pic_register(void) +static void pic_register_types(void) { type_register_static(&i8259_info); } -device_init(pic_register) +type_init(pic_register_types) diff --git a/hw/i8259_common.c b/hw/i8259_common.c index 9f150bc6d0..ab3d98b2a1 100644 --- a/hw/i8259_common.c +++ b/hw/i8259_common.c @@ -28,7 +28,7 @@ void pic_reset_common(PICCommonState *s) { s->last_irr = 0; - s->irr = 0; + s->irr &= s->elcr; s->imr = 0; s->isr = 0; s->priority_add = 0; @@ -153,9 +153,9 @@ static TypeInfo pic_common_type = { .abstract = true, }; -static void register_devices(void) +static void register_types(void) { type_register_static(&pic_common_type); } -device_init(register_devices); +type_init(register_types); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index c87a6ca515..041ce1e89f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -146,6 +146,7 @@ static void ahci_check_irq(AHCIState *s) DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus); + s->control_regs.irqstatus = 0; for (i = 0; i < s->ports; i++) { AHCIPortRegs *pr = &s->dev[i].port_regs; if (pr->irq_stat & pr->irq_mask) { @@ -216,6 +217,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) break; case PORT_IRQ_STAT: pr->irq_stat &= ~val; + ahci_check_irq(s); break; case PORT_IRQ_MASK: pr->irq_mask = val & 0xfdc000ff; @@ -426,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; @@ -560,6 +513,11 @@ static void ahci_reset_port(AHCIState *s, int port) ncq_tfs->aiocb = NULL; } + /* Maybe we just finished the request thanks to bdrv_aio_cancel() */ + if (!ncq_tfs->used) { + continue; + } + qemu_sglist_destroy(&ncq_tfs->sglist); ncq_tfs->used = 0; } @@ -795,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); @@ -809,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); @@ -1016,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 */ @@ -1060,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; @@ -1085,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 */ @@ -1261,9 +1212,9 @@ static TypeInfo sysbus_ahci_info = { .class_init = sysbus_ahci_class_init, }; -static void sysbus_ahci_register(void) +static void sysbus_ahci_register_types(void) { type_register_static(&sysbus_ahci_info); } -device_init(sysbus_ahci_register); +type_init(sysbus_ahci_register_types) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 0adb27b799..5919cf52d8 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/cmd646.c b/hw/ide/cmd646.c index a119500f40..743ec02406 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -351,8 +351,9 @@ static TypeInfo cmd646_ide_info = { .class_init = cmd646_ide_class_init, }; -static void cmd646_ide_register(void) +static void cmd646_ide_register_types(void) { type_register_static(&cmd646_ide_info); } -device_init(cmd646_ide_register); + +type_init(cmd646_ide_register_types) diff --git a/hw/ide/core.c b/hw/ide/core.c index 56b219b504..4d568acc9c 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; @@ -1068,6 +1068,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */ goto abort_cmd; } + if (!s->bs) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = 1; ide_sector_read(s); @@ -1078,6 +1081,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) case WIN_WRITE_ONCE: case CFA_WRITE_SECT_WO_ERASE: case WIN_WRITE_VERIFY: + if (!s->bs) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; @@ -1088,8 +1094,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) case WIN_MULTREAD_EXT: lba48 = 1; case WIN_MULTREAD: - if (!s->mult_sectors) + if (!s->bs) { + goto abort_cmd; + } + if (!s->mult_sectors) { goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = s->mult_sectors; ide_sector_read(s); @@ -1098,8 +1108,12 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_MULTWRITE: case CFA_WRITE_MULTI_WO_ERASE: - if (!s->mult_sectors) + if (!s->bs) { goto abort_cmd; + } + if (!s->mult_sectors) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; @@ -1114,8 +1128,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_READDMA: case WIN_READDMA_ONCE: - if (!s->bs) + if (!s->bs) { goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_READ); break; @@ -1123,8 +1138,9 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; case WIN_WRITEDMA: case WIN_WRITEDMA_ONCE: - if (!s->bs) + if (!s->bs) { goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_WRITE); s->media_changed = 1; @@ -2077,15 +2093,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 +2132,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/ide/ich.c b/hw/ide/ich.c index 5cdaa990ea..560ae37618 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -168,8 +168,9 @@ static TypeInfo ich_ahci_info = { .class_init = ich_ahci_class_init, }; -static void ich_ahci_register(void) +static void ich_ahci_register_types(void) { type_register_static(&ich_ahci_info); } -device_init(ich_ahci_register); + +type_init(ich_ahci_register_types) diff --git a/hw/ide/isa.c b/hw/ide/isa.c index a0bcb43eba..8ab2718eea 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -118,9 +118,9 @@ static TypeInfo isa_ide_info = { .class_init = isa_ide_class_initfn, }; -static void isa_ide_register_devices(void) +static void isa_ide_register_types(void) { type_register_static(&isa_ide_info); } -device_init(isa_ide_register_devices) +type_init(isa_ide_register_types) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 246dd5704b..88c0942e34 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -336,7 +336,7 @@ static uint64_t bmdma_addr_read(void *opaque, target_phys_addr_t addr, data = (bm->addr >> (addr * 8)) & mask; #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, (unsigned)*data); + printf("%s: 0x%08x\n", __func__, (unsigned)data); #endif return data; } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index bf4465bb49..1030fcc31c 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -53,7 +53,7 @@ static uint64_t bmdma_read(void *opaque, target_phys_addr_t addr, unsigned size) break; } #ifdef DEBUG_IDE - printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); + printf("bmdma: readb 0x%02x : 0x%02x\n", (uint8_t)addr, val); #endif return val; } @@ -68,7 +68,7 @@ static void bmdma_write(void *opaque, target_phys_addr_t addr, } #ifdef DEBUG_IDE - printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); + printf("bmdma: writeb 0x%02x : 0x%02x\n", (uint8_t)addr, (uint8_t)val); #endif switch(addr & 3) { case 0: @@ -299,10 +299,11 @@ static TypeInfo piix4_ide_info = { .class_init = piix4_ide_class_init, }; -static void piix_ide_register(void) +static void piix_ide_register_types(void) { type_register_static(&piix3_ide_info); type_register_static(&piix3_ide_xen_info); type_register_static(&piix4_ide_info); } -device_init(piix_ide_register); + +type_init(piix_ide_register_types) diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 1640616906..f6a48961c5 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -257,11 +257,12 @@ static TypeInfo ide_device_type_info = { .class_init = ide_device_class_init, }; -static void ide_dev_register(void) +static void ide_register_types(void) { type_register_static(&ide_hd_info); type_register_static(&ide_cd_info); type_register_static(&ide_drive_info); type_register_static(&ide_device_type_info); } -device_init(ide_dev_register); + +type_init(ide_register_types) diff --git a/hw/ide/via.c b/hw/ide/via.c index b4ca6f27b3..2886bc6dfb 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -234,8 +234,9 @@ static TypeInfo via_ide_info = { .class_init = via_ide_class_init, }; -static void via_ide_register(void) +static void via_ide_register_types(void) { type_register_static(&via_ide_info); } -device_init(via_ide_register); + +type_init(via_ide_register_types) diff --git a/hw/integratorcp.c b/hw/integratorcp.c index 6dbd649908..5b06c81c9b 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -8,7 +8,6 @@ */ #include "sysbus.h" -#include "primecell.h" #include "devices.h" #include "boards.h" #include "arm-misc.h" @@ -553,10 +552,10 @@ static TypeInfo icp_pic_info = { .class_init = icp_pic_class_init, }; -static void integratorcp_register_devices(void) +static void integratorcp_register_types(void) { type_register_static(&icp_pic_info); type_register_static(&core_info); } -device_init(integratorcp_register_devices) +type_init(integratorcp_register_types) diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 83c42d58b6..bb11af286a 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -1287,12 +1287,13 @@ static TypeInfo hda_codec_device_type_info = { .class_init = hda_codec_device_class_init, }; -static void intel_hda_register(void) +static void intel_hda_register_types(void) { type_register_static(&intel_hda_info); type_register_static(&hda_codec_device_type_info); } -device_init(intel_hda_register); + +type_init(intel_hda_register_types) /* * create intel hda controller with codec attached to it, diff --git a/hw/ioapic.c b/hw/ioapic.c index 79549f8ed1..3fee0114d9 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -251,9 +251,9 @@ static TypeInfo ioapic_info = { .class_init = ioapic_class_init, }; -static void ioapic_register_devices(void) +static void ioapic_register_types(void) { type_register_static(&ioapic_info); } -device_init(ioapic_register_devices) +type_init(ioapic_register_types) diff --git a/hw/ioapic_common.c b/hw/ioapic_common.c index f932700d12..653eef2ce1 100644 --- a/hw/ioapic_common.c +++ b/hw/ioapic_common.c @@ -112,10 +112,9 @@ static TypeInfo ioapic_common_type = { .abstract = true, }; -static void register_devices(void) +static void register_types(void) { type_register_static(&ioapic_common_type); } -device_init(register_devices); - +type_init(register_types) diff --git a/hw/ioh3420.c b/hw/ioh3420.c index 1c60123d43..1632d31c19 100644 --- a/hw/ioh3420.c +++ b/hw/ioh3420.c @@ -237,12 +237,12 @@ static TypeInfo ioh3420_info = { .class_init = ioh3420_class_init, }; -static void ioh3420_register(void) +static void ioh3420_register_types(void) { type_register_static(&ioh3420_info); } -device_init(ioh3420_register); +type_init(ioh3420_register_types) /* * Local variables: diff --git a/hw/isa-bus.c b/hw/isa-bus.c index d03f82824c..5a43f03a7c 100644 --- a/hw/isa-bus.c +++ b/hw/isa-bus.c @@ -210,7 +210,7 @@ static TypeInfo isa_device_type_info = { .class_init = isa_device_class_init, }; -static void isabus_register_devices(void) +static void isabus_register_types(void) { type_register_static(&isabus_bridge_info); type_register_static(&isa_device_type_info); @@ -235,4 +235,4 @@ MemoryRegion *isa_address_space(ISADevice *dev) return get_system_memory(); } -device_init(isabus_register_devices) +type_init(isabus_register_types) diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 6f017d474c..64e1cd968e 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -798,9 +798,9 @@ static TypeInfo ivshmem_info = { .class_init = ivshmem_class_init, }; -static void ivshmem_register_devices(void) +static void ivshmem_register_types(void) { type_register_static(&ivshmem_info); } -device_init(ivshmem_register_devices) +type_init(ivshmem_register_types) diff --git a/hw/jazz_led.c b/hw/jazz_led.c index f8a218252c..648652302a 100644 --- a/hw/jazz_led.c +++ b/hw/jazz_led.c @@ -1,7 +1,7 @@ /* * QEMU JAZZ LED emulator. * - * Copyright (c) 2007 Hervé Poussineau + * Copyright (c) 2007-2012 Herve Poussineau * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,131 +22,53 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" #include "console.h" #include "pixel_ops.h" - -//#define DEBUG_LED - -#ifdef DEBUG_LED -#define DPRINTF(fmt, ...) \ -do { printf("jazz led: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif -#define BADF(fmt, ...) \ -do { fprintf(stderr, "jazz led ERROR: " fmt , ## __VA_ARGS__);} while (0) +#include "trace.h" +#include "sysbus.h" typedef enum { REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2, } screen_state_t; typedef struct LedState { + SysBusDevice busdev; MemoryRegion iomem; uint8_t segments; DisplayState *ds; screen_state_t state; } LedState; -static uint32_t led_readb(void *opaque, target_phys_addr_t addr) +static uint64_t jazz_led_read(void *opaque, target_phys_addr_t addr, + unsigned int size) { LedState *s = opaque; - uint32_t val; - - switch (addr) { - case 0: - val = s->segments; - break; - default: - BADF("invalid read at [" TARGET_FMT_plx "]\n", addr); - val = 0; - } + uint8_t val; - DPRINTF("read addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val); + val = s->segments; + trace_jazz_led_read(addr, val); return val; } -static uint32_t led_readw(void *opaque, target_phys_addr_t addr) -{ - uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = led_readb(opaque, addr) << 8; - v |= led_readb(opaque, addr + 1); -#else - v = led_readb(opaque, addr); - v |= led_readb(opaque, addr + 1) << 8; -#endif - return v; -} - -static uint32_t led_readl(void *opaque, target_phys_addr_t addr) -{ - uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = led_readb(opaque, addr) << 24; - v |= led_readb(opaque, addr + 1) << 16; - v |= led_readb(opaque, addr + 2) << 8; - v |= led_readb(opaque, addr + 3); -#else - v = led_readb(opaque, addr); - v |= led_readb(opaque, addr + 1) << 8; - v |= led_readb(opaque, addr + 2) << 16; - v |= led_readb(opaque, addr + 3) << 24; -#endif - return v; -} - -static void led_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +static void jazz_led_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned int size) { LedState *s = opaque; + uint8_t new_val = val & 0xff; - DPRINTF("write addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val); + trace_jazz_led_write(addr, new_val); - switch (addr) { - case 0: - s->segments = val; - s->state |= REDRAW_SEGMENTS; - break; - default: - BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr); - break; - } -} - -static void led_writew(void *opaque, target_phys_addr_t addr, uint32_t val) -{ -#ifdef TARGET_WORDS_BIGENDIAN - led_writeb(opaque, addr, (val >> 8) & 0xff); - led_writeb(opaque, addr + 1, val & 0xff); -#else - led_writeb(opaque, addr, val & 0xff); - led_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif -} - -static void led_writel(void *opaque, target_phys_addr_t addr, uint32_t val) -{ -#ifdef TARGET_WORDS_BIGENDIAN - led_writeb(opaque, addr, (val >> 24) & 0xff); - led_writeb(opaque, addr + 1, (val >> 16) & 0xff); - led_writeb(opaque, addr + 2, (val >> 8) & 0xff); - led_writeb(opaque, addr + 3, val & 0xff); -#else - led_writeb(opaque, addr, val & 0xff); - led_writeb(opaque, addr + 1, (val >> 8) & 0xff); - led_writeb(opaque, addr + 2, (val >> 16) & 0xff); - led_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif + s->segments = new_val; + s->state |= REDRAW_SEGMENTS; } static const MemoryRegionOps led_ops = { - .old_mmio = { - .read = { led_readb, led_readw, led_readl, }, - .write = { led_writeb, led_writew, led_writel, }, - }, + .read = jazz_led_read, + .write = jazz_led_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 1, + .impl.max_access_size = 1, }; /***********************************************************/ @@ -283,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; @@ -304,20 +221,71 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata) dpy_update(s->ds, 0, 0, 2, 1); } -void jazz_led_init(MemoryRegion *address_space, target_phys_addr_t base) +static int jazz_led_post_load(void *opaque, int version_id) { - LedState *s; + /* force refresh */ + jazz_led_invalidate_display(opaque); - s = g_malloc0(sizeof(LedState)); + return 0; +} - s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND; +static const VMStateDescription vmstate_jazz_led = { + .name = "jazz-led", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = jazz_led_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(segments, LedState), + VMSTATE_END_OF_LIST() + } +}; + +static int jazz_led_init(SysBusDevice *dev) +{ + LedState *s = FROM_SYSBUS(LedState, dev); memory_region_init_io(&s->iomem, &led_ops, s, "led", 1); - memory_region_add_subregion(address_space, base, &s->iomem); + sysbus_init_mmio(dev, &s->iomem); 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; +} + +static void jazz_led_reset(DeviceState *d) +{ + LedState *s = DO_UPCAST(LedState, busdev.qdev, d); + + s->segments = 0; + s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND; qemu_console_resize(s->ds, 60, 80); } + +static void jazz_led_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = jazz_led_init; + dc->desc = "Jazz LED display", + dc->vmsd = &vmstate_jazz_led; + dc->reset = jazz_led_reset; +} + +static TypeInfo jazz_led_info = { + .name = "jazz-led", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LedState), + .class_init = jazz_led_class_init, +}; + +static void jazz_led_register(void) +{ + type_register_static(&jazz_led_info); +} + +type_init(jazz_led_register); diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c index dfc2ab3b03..9ca68f81aa 100644 --- a/hw/kvm/apic.c +++ b/hw/kvm/apic.c @@ -92,6 +92,35 @@ static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) s->tpr = (val & 0x0f) << 4; } +static uint8_t kvm_apic_get_tpr(APICCommonState *s) +{ + return s->tpr >> 4; +} + +static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) +{ + struct kvm_tpr_access_ctl ctl = { + .enabled = enable + }; + + kvm_vcpu_ioctl(s->cpu_env, KVM_TPR_ACCESS_REPORTING, &ctl); +} + +static void kvm_apic_vapic_base_update(APICCommonState *s) +{ + struct kvm_vapic_addr vapid_addr = { + .vapic_addr = s->vapic_paddr, + }; + int ret; + + ret = kvm_vcpu_ioctl(s->cpu_env, KVM_SET_VAPIC_ADDR, &vapid_addr); + if (ret < 0) { + fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", + strerror(-ret)); + abort(); + } +} + static void do_inject_external_nmi(void *data) { APICCommonState *s = data; @@ -129,6 +158,9 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data) k->init = kvm_apic_init; k->set_base = kvm_apic_set_base; k->set_tpr = kvm_apic_set_tpr; + k->get_tpr = kvm_apic_get_tpr; + k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting; + k->vapic_base_update = kvm_apic_vapic_base_update; k->external_nmi = kvm_apic_external_nmi; } @@ -139,9 +171,9 @@ static TypeInfo kvm_apic_info = { .class_init = kvm_apic_class_init, }; -static void kvm_apic_register_device(void) +static void kvm_apic_register_types(void) { type_register_static(&kvm_apic_info); } -device_init(kvm_apic_register_device) +type_init(kvm_apic_register_types) diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c index d5a53869b3..2157340326 100644 --- a/hw/kvm/clock.c +++ b/hw/kvm/clock.c @@ -119,11 +119,11 @@ void kvmclock_create(void) } } -static void kvmclock_register_device(void) +static void kvmclock_register_types(void) { if (kvm_enabled()) { type_register_static(&kvmclock_info); } } -device_init(kvmclock_register_device); +type_init(kvmclock_register_types) diff --git a/hw/kvm/i8259.c b/hw/kvm/i8259.c index 14bd427518..94d1b9aa95 100644 --- a/hw/kvm/i8259.c +++ b/hw/kvm/i8259.c @@ -84,8 +84,8 @@ static void kvm_pic_reset(DeviceState *dev) { PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev); - pic_reset_common(s); s->elcr = 0; + pic_reset_common(s); kvm_pic_put(s); } @@ -130,9 +130,9 @@ static TypeInfo kvm_i8259_info = { .class_init = kvm_i8259_class_init, }; -static void kvm_pic_register(void) +static void kvm_pic_register_types(void) { type_register_static(&kvm_i8259_info); } -device_init(kvm_pic_register) +type_init(kvm_pic_register_types) diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c index b316933a96..3ae3175403 100644 --- a/hw/kvm/ioapic.c +++ b/hw/kvm/ioapic.c @@ -117,9 +117,9 @@ static TypeInfo kvm_ioapic_info = { .class_init = kvm_ioapic_class_init, }; -static void kvm_ioapic_register_device(void) +static void kvm_ioapic_register_types(void) { type_register_static(&kvm_ioapic_info); } -device_init(kvm_ioapic_register_device) +type_init(kvm_ioapic_register_types) diff --git a/hw/kvmvapic.c b/hw/kvmvapic.c new file mode 100644 index 0000000000..36ccfbcdbd --- /dev/null +++ b/hw/kvmvapic.c @@ -0,0 +1,805 @@ +/* + * TPR optimization for 32-bit Windows guests (XP and Server 2003) + * + * Copyright (C) 2007-2008 Qumranet Technologies + * Copyright (C) 2012 Jan Kiszka, Siemens AG + * + * This work is licensed under the terms of the GNU GPL version 2, or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "sysemu.h" +#include "cpus.h" +#include "kvm.h" +#include "apic_internal.h" + +#define APIC_DEFAULT_ADDRESS 0xfee00000 + +#define VAPIC_IO_PORT 0x7e + +#define VAPIC_CPU_SHIFT 7 + +#define ROM_BLOCK_SIZE 512 +#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) + +typedef enum VAPICMode { + VAPIC_INACTIVE = 0, + VAPIC_ACTIVE = 1, + VAPIC_STANDBY = 2, +} VAPICMode; + +typedef struct VAPICHandlers { + uint32_t set_tpr; + uint32_t set_tpr_eax; + uint32_t get_tpr[8]; + uint32_t get_tpr_stack; +} QEMU_PACKED VAPICHandlers; + +typedef struct GuestROMState { + char signature[8]; + uint32_t vaddr; + uint32_t fixup_start; + uint32_t fixup_end; + uint32_t vapic_vaddr; + uint32_t vapic_size; + uint32_t vcpu_shift; + uint32_t real_tpr_addr; + VAPICHandlers up; + VAPICHandlers mp; +} QEMU_PACKED GuestROMState; + +typedef struct VAPICROMState { + SysBusDevice busdev; + MemoryRegion io; + MemoryRegion rom; + uint32_t state; + uint32_t rom_state_paddr; + uint32_t rom_state_vaddr; + uint32_t vapic_paddr; + uint32_t real_tpr_addr; + GuestROMState rom_state; + size_t rom_size; + bool rom_mapped_writable; +} VAPICROMState; + +#define TPR_INSTR_ABS_MODRM 0x1 +#define TPR_INSTR_MATCH_MODRM_REG 0x2 + +typedef struct TPRInstruction { + uint8_t opcode; + uint8_t modrm_reg; + unsigned int flags; + TPRAccess access; + size_t length; + off_t addr_offset; +} TPRInstruction; + +/* must be sorted by length, shortest first */ +static const TPRInstruction tpr_instr[] = { + { /* mov abs to eax */ + .opcode = 0xa1, + .access = TPR_ACCESS_READ, + .length = 5, + .addr_offset = 1, + }, + { /* mov eax to abs */ + .opcode = 0xa3, + .access = TPR_ACCESS_WRITE, + .length = 5, + .addr_offset = 1, + }, + { /* mov r32 to r/m32 */ + .opcode = 0x89, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_WRITE, + .length = 6, + .addr_offset = 2, + }, + { /* mov r/m32 to r32 */ + .opcode = 0x8b, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* push r/m32 */ + .opcode = 0xff, + .modrm_reg = 6, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* mov imm32, r/m32 (c7/0) */ + .opcode = 0xc7, + .modrm_reg = 0, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_WRITE, + .length = 10, + .addr_offset = 2, + }, +}; + +static void read_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state, + sizeof(GuestROMState), 0); +} + +static void write_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_rw(s->rom_state_paddr, (void *)&s->rom_state, + sizeof(GuestROMState), 1); +} + +static void update_guest_rom_state(VAPICROMState *s) +{ + read_guest_rom_state(s); + + s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); + s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); + + write_guest_rom_state(s); +} + +static int find_real_tpr_addr(VAPICROMState *s, CPUState *env) +{ + target_phys_addr_t paddr; + target_ulong addr; + + if (s->state == VAPIC_ACTIVE) { + return 0; + } + /* + * If there is no prior TPR access instruction we could analyze (which is + * the case after resume from hibernation), we need to scan the possible + * virtual address space for the APIC mapping. + */ + for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { + paddr = cpu_get_phys_page_debug(env, addr); + if (paddr != APIC_DEFAULT_ADDRESS) { + continue; + } + s->real_tpr_addr = addr + 0x80; + update_guest_rom_state(s); + return 0; + } + return -1; +} + +static uint8_t modrm_reg(uint8_t modrm) +{ + return (modrm >> 3) & 7; +} + +static bool is_abs_modrm(uint8_t modrm) +{ + return (modrm & 0xc7) == 0x05; +} + +static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) +{ + return opcode[0] == instr->opcode && + (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) && + (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || + modrm_reg(opcode[1]) == instr->modrm_reg); +} + +static int evaluate_tpr_instruction(VAPICROMState *s, CPUState *env, + target_ulong *pip, TPRAccess access) +{ + const TPRInstruction *instr; + target_ulong ip = *pip; + uint8_t opcode[2]; + uint32_t real_tpr_addr; + int i; + + if ((ip & 0xf0000000ULL) != 0x80000000ULL && + (ip & 0xf0000000ULL) != 0xe0000000ULL) { + return -1; + } + + /* + * Early Windows 2003 SMP initialization contains a + * + * mov imm32, r/m32 + * + * instruction that is patched by TPR optimization. The problem is that + * RSP, used by the patched instruction, is zero, so the guest gets a + * double fault and dies. + */ + if (env->regs[R_ESP] == 0) { + return -1; + } + + if (kvm_enabled() && !kvm_irqchip_in_kernel()) { + /* + * KVM without kernel-based TPR access reporting will pass an IP that + * points after the accessing instruction. So we need to look backward + * to find the reason. + */ + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (instr->access != access) { + continue; + } + if (cpu_memory_rw_debug(env, ip - instr->length, opcode, + sizeof(opcode), 0) < 0) { + return -1; + } + if (opcode_matches(opcode, instr)) { + ip -= instr->length; + goto instruction_ok; + } + } + return -1; + } else { + if (cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0) < 0) { + return -1; + } + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (opcode_matches(opcode, instr)) { + goto instruction_ok; + } + } + return -1; + } + +instruction_ok: + /* + * Grab the virtual TPR address from the instruction + * and update the cached values. + */ + if (cpu_memory_rw_debug(env, ip + instr->addr_offset, + (void *)&real_tpr_addr, + sizeof(real_tpr_addr), 0) < 0) { + return -1; + } + real_tpr_addr = le32_to_cpu(real_tpr_addr); + if ((real_tpr_addr & 0xfff) != 0x80) { + return -1; + } + s->real_tpr_addr = real_tpr_addr; + update_guest_rom_state(s); + + *pip = ip; + return 0; +} + +static int update_rom_mapping(VAPICROMState *s, CPUState *env, target_ulong ip) +{ + target_phys_addr_t paddr; + uint32_t rom_state_vaddr; + uint32_t pos, patch, offset; + + /* nothing to do if already activated */ + if (s->state == VAPIC_ACTIVE) { + return 0; + } + + /* bail out if ROM init code was not executed (missing ROM?) */ + if (s->state == VAPIC_INACTIVE) { + return -1; + } + + /* find out virtual address of the ROM */ + rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000); + paddr = cpu_get_phys_page_debug(env, rom_state_vaddr); + if (paddr == -1) { + return -1; + } + paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; + if (paddr != s->rom_state_paddr) { + return -1; + } + read_guest_rom_state(s); + if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { + return -1; + } + s->rom_state_vaddr = rom_state_vaddr; + + /* fixup addresses in ROM if needed */ + if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) { + return 0; + } + for (pos = le32_to_cpu(s->rom_state.fixup_start); + pos < le32_to_cpu(s->rom_state.fixup_end); + pos += 4) { + cpu_physical_memory_rw(paddr + pos - s->rom_state.vaddr, + (void *)&offset, sizeof(offset), 0); + offset = le32_to_cpu(offset); + cpu_physical_memory_rw(paddr + offset, (void *)&patch, + sizeof(patch), 0); + patch = le32_to_cpu(patch); + patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); + patch = cpu_to_le32(patch); + cpu_physical_memory_rw(paddr + offset, (void *)&patch, + sizeof(patch), 1); + } + read_guest_rom_state(s); + s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - + le32_to_cpu(s->rom_state.vaddr); + + return 0; +} + +/* + * Tries to read the unique processor number from the Kernel Processor Control + * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR + * cannot be accessed or is considered invalid. This also ensures that we are + * not patching the wrong guest. + */ +static int get_kpcr_number(CPUState *env) +{ + struct kpcr { + uint8_t fill1[0x1c]; + uint32_t self; + uint8_t fill2[0x31]; + uint8_t number; + } QEMU_PACKED kpcr; + + if (cpu_memory_rw_debug(env, env->segs[R_FS].base, + (void *)&kpcr, sizeof(kpcr), 0) < 0 || + kpcr.self != env->segs[R_FS].base) { + return -1; + } + return kpcr.number; +} + +static int vapic_enable(VAPICROMState *s, CPUState *env) +{ + int cpu_number = get_kpcr_number(env); + target_phys_addr_t vapic_paddr; + static const uint8_t enabled = 1; + + if (cpu_number < 0) { + return -1; + } + vapic_paddr = s->vapic_paddr + + (((target_phys_addr_t)cpu_number) << VAPIC_CPU_SHIFT); + cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled), + (void *)&enabled, sizeof(enabled), 1); + apic_enable_vapic(env->apic_state, vapic_paddr); + + s->state = VAPIC_ACTIVE; + + return 0; +} + +static void patch_byte(CPUState *env, target_ulong addr, uint8_t byte) +{ + cpu_memory_rw_debug(env, addr, &byte, 1, 1); +} + +static void patch_call(VAPICROMState *s, CPUState *env, target_ulong ip, + uint32_t target) +{ + uint32_t offset; + + offset = cpu_to_le32(target - ip - 5); + patch_byte(env, ip, 0xe8); /* call near */ + cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1); +} + +static void patch_instruction(VAPICROMState *s, CPUState *env, target_ulong ip) +{ + target_phys_addr_t paddr; + VAPICHandlers *handlers; + uint8_t opcode[2]; + uint32_t imm32; + + if (smp_cpus == 1) { + handlers = &s->rom_state.up; + } else { + handlers = &s->rom_state.mp; + } + + pause_all_vcpus(); + + cpu_memory_rw_debug(env, ip, opcode, sizeof(opcode), 0); + + switch (opcode[0]) { + case 0x89: /* mov r32 to r/m32 */ + patch_byte(env, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(s, env, ip + 1, handlers->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + patch_byte(env, ip, 0x90); + patch_call(s, env, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(s, env, ip, handlers->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(s, env, ip, handlers->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + patch_byte(env, ip, 0x68); /* push imm32 */ + cpu_memory_rw_debug(env, ip + 6, (void *)&imm32, sizeof(imm32), 0); + cpu_memory_rw_debug(env, ip + 1, (void *)&imm32, sizeof(imm32), 1); + patch_call(s, env, ip + 5, handlers->set_tpr); + break; + case 0xff: /* push r/m32 */ + patch_byte(env, ip, 0x50); /* push eax */ + patch_call(s, env, ip + 1, handlers->get_tpr_stack); + break; + default: + abort(); + } + + resume_all_vcpus(); + + paddr = cpu_get_phys_page_debug(env, ip); + paddr += ip & ~TARGET_PAGE_MASK; + tb_invalidate_phys_page_range(paddr, paddr + 1, 1); +} + +void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, + TPRAccess access) +{ + VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); + CPUState *env = cpu; + + cpu_synchronize_state(env); + + if (evaluate_tpr_instruction(s, env, &ip, access) < 0) { + if (s->state == VAPIC_ACTIVE) { + vapic_enable(s, env); + } + return; + } + if (update_rom_mapping(s, env, ip) < 0) { + return; + } + if (vapic_enable(s, env) < 0) { + return; + } + patch_instruction(s, env, ip); +} + +typedef struct VAPICEnableTPRReporting { + DeviceState *apic; + bool enable; +} VAPICEnableTPRReporting; + +static void vapic_do_enable_tpr_reporting(void *data) +{ + VAPICEnableTPRReporting *info = data; + + apic_enable_tpr_access_reporting(info->apic, info->enable); +} + +static void vapic_enable_tpr_reporting(bool enable) +{ + VAPICEnableTPRReporting info = { + .enable = enable, + }; + CPUState *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + info.apic = env->apic_state; + run_on_cpu(env, vapic_do_enable_tpr_reporting, &info); + } +} + +static void vapic_reset(DeviceState *dev) +{ + VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); + + if (s->state == VAPIC_ACTIVE) { + s->state = VAPIC_STANDBY; + } + vapic_enable_tpr_reporting(false); +} + +/* + * Set the IRQ polling hypercalls to the supported variant: + * - vmcall if using KVM in-kernel irqchip + * - 32-bit VAPIC port write otherwise + */ +static int patch_hypercalls(VAPICROMState *s) +{ + target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + static const uint8_t vmcall_pattern[] = { /* vmcall */ + 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 + }; + static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ + 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e + }; + uint8_t alternates[2]; + const uint8_t *pattern; + const uint8_t *patch; + int patches = 0; + off_t pos; + uint8_t *rom; + + rom = g_malloc(s->rom_size); + cpu_physical_memory_rw(rom_paddr, rom, s->rom_size, 0); + + for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { + if (kvm_irqchip_in_kernel()) { + pattern = outl_pattern; + alternates[0] = outl_pattern[7]; + alternates[1] = outl_pattern[7]; + patch = &vmcall_pattern[5]; + } else { + pattern = vmcall_pattern; + alternates[0] = vmcall_pattern[7]; + alternates[1] = 0xd9; /* AMD's VMMCALL */ + patch = &outl_pattern[5]; + } + if (memcmp(rom + pos, pattern, 7) == 0 && + (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { + cpu_physical_memory_rw(rom_paddr + pos + 5, (uint8_t *)patch, + 3, 1); + /* + * Don't flush the tb here. Under ordinary conditions, the patched + * calls are miles away from the current IP. Under malicious + * conditions, the guest could trick us to crash. + */ + } + } + + g_free(rom); + + if (patches != 0 && patches != 2) { + return -1; + } + + return 0; +} + +/* + * For TCG mode or the time KVM honors read-only memory regions, we need to + * enable write access to the option ROM so that variables can be updated by + * the guest. + */ +static void vapic_map_rom_writable(VAPICROMState *s) +{ + target_phys_addr_t rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + MemoryRegionSection section; + MemoryRegion *as; + size_t rom_size; + uint8_t *ram; + + as = sysbus_address_space(&s->busdev); + + if (s->rom_mapped_writable) { + memory_region_del_subregion(as, &s->rom); + memory_region_destroy(&s->rom); + } + + /* grab RAM memory region (region @rom_paddr may still be pc.rom) */ + section = memory_region_find(as, 0, 1); + + /* read ROM size from RAM region */ + ram = memory_region_get_ram_ptr(section.mr); + rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; + s->rom_size = rom_size; + + /* We need to round up to avoid creating subpages + * from which we cannot run code. */ + rom_size = TARGET_PAGE_ALIGN(rom_size); + + memory_region_init_alias(&s->rom, "kvmvapic-rom", section.mr, rom_paddr, + rom_size); + memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000); + s->rom_mapped_writable = true; +} + +static int vapic_prepare(VAPICROMState *s) +{ + vapic_map_rom_writable(s); + + if (patch_hypercalls(s) < 0) { + return -1; + } + + vapic_enable_tpr_reporting(true); + + return 0; +} + +static void vapic_write(void *opaque, target_phys_addr_t addr, uint64_t data, + unsigned int size) +{ + CPUState *env = cpu_single_env; + target_phys_addr_t rom_paddr; + VAPICROMState *s = opaque; + + cpu_synchronize_state(env); + + /* + * The VAPIC supports two PIO-based hypercalls, both via port 0x7E. + * o 16-bit write access: + * Reports the option ROM initialization to the hypervisor. Written + * value is the offset of the state structure in the ROM. + * o 8-bit write access: + * Reactivates the VAPIC after a guest hibernation, i.e. after the + * option ROM content has been re-initialized by a guest power cycle. + * o 32-bit write access: + * Poll for pending IRQs, considering the current VAPIC state. + */ + switch (size) { + case 2: + if (s->state == VAPIC_INACTIVE) { + rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; + s->rom_state_paddr = rom_paddr + data; + + s->state = VAPIC_STANDBY; + } + if (vapic_prepare(s) < 0) { + s->state = VAPIC_INACTIVE; + break; + } + break; + case 1: + if (kvm_enabled()) { + /* + * Disable triggering instruction in ROM by writing a NOP. + * + * We cannot do this in TCG mode as the reported IP is not + * accurate. + */ + pause_all_vcpus(); + patch_byte(env, env->eip - 2, 0x66); + patch_byte(env, env->eip - 1, 0x90); + resume_all_vcpus(); + } + + if (s->state == VAPIC_ACTIVE) { + break; + } + if (update_rom_mapping(s, env, env->eip) < 0) { + break; + } + if (find_real_tpr_addr(s, env) < 0) { + break; + } + vapic_enable(s, env); + break; + default: + case 4: + if (!kvm_irqchip_in_kernel()) { + apic_poll_irq(env->apic_state); + } + break; + } +} + +static const MemoryRegionOps vapic_ops = { + .write = vapic_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int vapic_init(SysBusDevice *dev) +{ + VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev); + + memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2); + sysbus_add_io(dev, VAPIC_IO_PORT, &s->io); + sysbus_init_ioports(dev, VAPIC_IO_PORT, 2); + + option_rom[nb_option_roms].name = "kvmvapic.bin"; + option_rom[nb_option_roms].bootindex = -1; + nb_option_roms++; + + return 0; +} + +static void do_vapic_enable(void *data) +{ + VAPICROMState *s = data; + + vapic_enable(s, first_cpu); +} + +static int vapic_post_load(void *opaque, int version_id) +{ + VAPICROMState *s = opaque; + uint8_t *zero; + + /* + * The old implementation of qemu-kvm did not provide the state + * VAPIC_STANDBY. Reconstruct it. + */ + if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { + s->state = VAPIC_STANDBY; + } + + if (s->state != VAPIC_INACTIVE) { + if (vapic_prepare(s) < 0) { + return -1; + } + } + if (s->state == VAPIC_ACTIVE) { + if (smp_cpus == 1) { + run_on_cpu(first_cpu, do_vapic_enable, s); + } else { + zero = g_malloc0(s->rom_state.vapic_size); + cpu_physical_memory_rw(s->vapic_paddr, zero, + s->rom_state.vapic_size, 1); + g_free(zero); + } + } + + return 0; +} + +static const VMStateDescription vmstate_handlers = { + .name = "kvmvapic-handlers", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(set_tpr, VAPICHandlers), + VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), + VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), + VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_guest_rom = { + .name = "kvmvapic-guest-rom", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UNUSED(8), /* signature */ + VMSTATE_UINT32(vaddr, GuestROMState), + VMSTATE_UINT32(fixup_start, GuestROMState), + VMSTATE_UINT32(fixup_end, GuestROMState), + VMSTATE_UINT32(vapic_vaddr, GuestROMState), + VMSTATE_UINT32(vapic_size, GuestROMState), + VMSTATE_UINT32(vcpu_shift, GuestROMState), + VMSTATE_UINT32(real_tpr_addr, GuestROMState), + VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vapic = { + .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vapic_post_load, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, + GuestROMState), + VMSTATE_UINT32(state, VAPICROMState), + VMSTATE_UINT32(real_tpr_addr, VAPICROMState), + VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), + VMSTATE_UINT32(vapic_paddr, VAPICROMState), + VMSTATE_UINT32(rom_state_paddr, VAPICROMState), + VMSTATE_END_OF_LIST() + } +}; + +static void vapic_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->no_user = 1; + dc->reset = vapic_reset; + dc->vmsd = &vmstate_vapic; + sc->init = vapic_init; +} + +static TypeInfo vapic_type = { + .name = "kvmvapic", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VAPICROMState), + .class_init = vapic_class_init, +}; + +static void vapic_register(void) +{ + type_register_static(&vapic_type); +} + +type_init(vapic_register); diff --git a/hw/lan9118.c b/hw/lan9118.c index 78777c7336..7b4fe87fca 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -235,11 +235,21 @@ typedef struct { int32_t rxp_offset; int32_t rxp_size; int32_t rxp_pad; + + uint32_t write_word_prev_offset; + uint32_t write_word_n; + uint16_t write_word_l; + uint16_t write_word_h; + uint32_t read_word_prev_offset; + uint32_t read_word_n; + uint32_t read_long; + + uint32_t mode_16bit; } lan9118_state; static const VMStateDescription vmstate_lan9118 = { .name = "lan9118", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_PTIMER(timer, lan9118_state), @@ -294,6 +304,14 @@ static const VMStateDescription vmstate_lan9118 = { VMSTATE_INT32(rxp_offset, lan9118_state), VMSTATE_INT32(rxp_size, lan9118_state), VMSTATE_INT32(rxp_pad, lan9118_state), + VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2), + VMSTATE_UINT32_V(write_word_n, lan9118_state, 2), + VMSTATE_UINT16_V(write_word_l, lan9118_state, 2), + VMSTATE_UINT16_V(write_word_h, lan9118_state, 2), + VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2), + VMSTATE_UINT32_V(read_word_n, lan9118_state, 2), + VMSTATE_UINT32_V(read_long, lan9118_state, 2), + VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2), VMSTATE_END_OF_LIST() } }; @@ -390,7 +408,7 @@ static void lan9118_reset(DeviceState *d) s->fifo_int = 0x48000000; s->rx_cfg = 0; s->tx_cfg = 0; - s->hw_cfg = 0x00050000; + s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004; s->pmt_ctrl &= 0x45; s->gpio_cfg = 0; s->txp->fifo_used = 0; @@ -429,6 +447,9 @@ static void lan9118_reset(DeviceState *d) s->mac_mii_data = 0; s->mac_flow = 0; + s->read_word_n = 0; + s->write_word_n = 0; + phy_reset(s); s->eeprom_writable = 0; @@ -984,7 +1005,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset, { lan9118_state *s = (lan9118_state *)opaque; offset &= 0xff; - + //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val); if (offset >= 0x20 && offset < 0x40) { /* TX FIFO */ @@ -1034,7 +1055,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset, /* SRST */ lan9118_reset(&s->busdev.qdev); } else { - s->hw_cfg = val & 0x003f300; + s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4); } break; case CSR_RX_DP_CTRL: @@ -1113,6 +1134,46 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset, lan9118_update(s); } +static void lan9118_writew(void *opaque, target_phys_addr_t offset, + uint32_t val) +{ + lan9118_state *s = (lan9118_state *)opaque; + offset &= 0xff; + + if (s->write_word_prev_offset != (offset & ~0x3)) { + /* New offset, reset word counter */ + s->write_word_n = 0; + s->write_word_prev_offset = offset & ~0x3; + } + + if (offset & 0x2) { + s->write_word_h = val; + } else { + s->write_word_l = val; + } + + //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val); + s->write_word_n++; + if (s->write_word_n == 2) { + s->write_word_n = 0; + lan9118_writel(s, offset & ~3, s->write_word_l + + (s->write_word_h << 16), 4); + } +} + +static void lan9118_16bit_mode_write(void *opaque, target_phys_addr_t offset, + uint64_t val, unsigned size) +{ + switch (size) { + case 2: + return lan9118_writew(opaque, offset, (uint32_t)val); + case 4: + return lan9118_writel(opaque, offset, val, size); + } + + hw_error("lan9118_write: Bad size 0x%x\n", size); +} + static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset, unsigned size) { @@ -1149,7 +1210,7 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset, case CSR_TX_CFG: return s->tx_cfg; case CSR_HW_CFG: - return s->hw_cfg | 0x4; + return s->hw_cfg; case CSR_RX_DP_CTRL: return 0; case CSR_RX_FIFO_INF: @@ -1187,12 +1248,60 @@ static uint64_t lan9118_readl(void *opaque, target_phys_addr_t offset, return 0; } +static uint32_t lan9118_readw(void *opaque, target_phys_addr_t offset) +{ + lan9118_state *s = (lan9118_state *)opaque; + uint32_t val; + + if (s->read_word_prev_offset != (offset & ~0x3)) { + /* New offset, reset word counter */ + s->read_word_n = 0; + s->read_word_prev_offset = offset & ~0x3; + } + + s->read_word_n++; + if (s->read_word_n == 1) { + s->read_long = lan9118_readl(s, offset & ~3, 4); + } else { + s->read_word_n = 0; + } + + if (offset & 2) { + val = s->read_long >> 16; + } else { + val = s->read_long & 0xFFFF; + } + + //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val); + return val; +} + +static uint64_t lan9118_16bit_mode_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + switch (size) { + case 2: + return lan9118_readw(opaque, offset); + case 4: + return lan9118_readl(opaque, offset, size); + } + + hw_error("lan9118_read: Bad size 0x%x\n", size); + return 0; +} + static const MemoryRegionOps lan9118_mem_ops = { .read = lan9118_readl, .write = lan9118_writel, .endianness = DEVICE_NATIVE_ENDIAN, }; +static const MemoryRegionOps lan9118_16bit_mem_ops = { + .read = lan9118_16bit_mode_read, + .write = lan9118_16bit_mode_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static void lan9118_cleanup(VLANClientState *nc) { lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; @@ -1214,8 +1323,10 @@ static int lan9118_init1(SysBusDevice *dev) lan9118_state *s = FROM_SYSBUS(lan9118_state, dev); QEMUBH *bh; int i; + const MemoryRegionOps *mem_ops = + s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; - memory_region_init_io(&s->mmio, &lan9118_mem_ops, s, "lan9118-mmio", 0x100); + memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100); sysbus_init_mmio(dev, &s->mmio); sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); @@ -1240,6 +1351,7 @@ static int lan9118_init1(SysBusDevice *dev) static Property lan9118_properties[] = { DEFINE_NIC_PROPERTIES(lan9118_state, conf), + DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -1261,7 +1373,7 @@ static TypeInfo lan9118_info = { .class_init = lan9118_class_init, }; -static void lan9118_register_devices(void) +static void lan9118_register_types(void) { type_register_static(&lan9118_info); } @@ -1282,4 +1394,4 @@ void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq) sysbus_connect_irq(s, 0, irq); } -device_init(lan9118_register_devices) +type_init(lan9118_register_types) diff --git a/hw/lance.c b/hw/lance.c index 519720bd9c..ce3d46c17b 100644 --- a/hw/lance.c +++ b/hw/lance.c @@ -162,8 +162,9 @@ static TypeInfo lance_info = { .class_init = lance_class_init, }; -static void lance_register_devices(void) +static void lance_register_types(void) { type_register_static(&lance_info); } -device_init(lance_register_devices) + +type_init(lance_register_types) diff --git a/hw/lm32_juart.c b/hw/lm32_juart.c index 38dd28230d..f07ed3977f 100644 --- a/hw/lm32_juart.c +++ b/hw/lm32_juart.c @@ -151,9 +151,9 @@ static TypeInfo lm32_juart_info = { .class_init = lm32_juart_class_init, }; -static void lm32_juart_register(void) +static void lm32_juart_register_types(void) { type_register_static(&lm32_juart_info); } -device_init(lm32_juart_register) +type_init(lm32_juart_register_types) diff --git a/hw/lm32_pic.c b/hw/lm32_pic.c index 7be6d0d68e..32f65db7f1 100644 --- a/hw/lm32_pic.c +++ b/hw/lm32_pic.c @@ -191,9 +191,9 @@ static TypeInfo lm32_pic_info = { .class_init = lm32_pic_class_init, }; -static void lm32_pic_register(void) +static void lm32_pic_register_types(void) { type_register_static(&lm32_pic_info); } -device_init(lm32_pic_register) +type_init(lm32_pic_register_types) diff --git a/hw/lm32_sys.c b/hw/lm32_sys.c index ba6f4acbaa..bbe03c41d5 100644 --- a/hw/lm32_sys.c +++ b/hw/lm32_sys.c @@ -164,9 +164,9 @@ static TypeInfo lm32_sys_info = { .class_init = lm32_sys_class_init, }; -static void lm32_sys_register(void) +static void lm32_sys_register_types(void) { type_register_static(&lm32_sys_info); } -device_init(lm32_sys_register) +type_init(lm32_sys_register_types) diff --git a/hw/lm32_timer.c b/hw/lm32_timer.c index 3cb4e0a4f3..e9450a0ce1 100644 --- a/hw/lm32_timer.c +++ b/hw/lm32_timer.c @@ -222,9 +222,9 @@ static TypeInfo lm32_timer_info = { .class_init = lm32_timer_class_init, }; -static void lm32_timer_register(void) +static void lm32_timer_register_types(void) { type_register_static(&lm32_timer_info); } -device_init(lm32_timer_register) +type_init(lm32_timer_register_types) diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c index 630ccb7131..57066e28c6 100644 --- a/hw/lm32_uart.c +++ b/hw/lm32_uart.c @@ -288,9 +288,9 @@ static TypeInfo lm32_uart_info = { .class_init = lm32_uart_class_init, }; -static void lm32_uart_register(void) +static void lm32_uart_register_types(void) { type_register_static(&lm32_uart_info); } -device_init(lm32_uart_register) +type_init(lm32_uart_register_types) diff --git a/hw/lm832x.c b/hw/lm832x.c index 895d306635..8e09f9bcc9 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -513,9 +513,9 @@ static TypeInfo lm8323_info = { .class_init = lm8323_class_init, }; -static void lm832x_register_devices(void) +static void lm832x_register_types(void) { type_register_static(&lm8323_info); } -device_init(lm832x_register_devices) +type_init(lm832x_register_types) diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 9a7ffe3f42..edc09b7307 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; @@ -2142,9 +2142,9 @@ static TypeInfo lsi_info = { .class_init = lsi_class_init, }; -static void lsi53c895a_register_devices(void) +static void lsi53c895a_register_types(void) { type_register_static(&lsi_info); } -device_init(lsi53c895a_register_devices); +type_init(lsi53c895a_register_types) diff --git a/hw/m48t59.c b/hw/m48t59.c index c35867d0a1..60bbb00946 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -768,10 +768,10 @@ static TypeInfo m48t59_info = { .class_init = m48t59_class_init, }; -static void m48t59_register_devices(void) +static void m48t59_register_types(void) { type_register_static(&m48t59_info); type_register_static(&m48t59_isa_info); } -device_init(m48t59_register_devices) +type_init(m48t59_register_types) diff --git a/hw/macio.c b/hw/macio.c index 3d648e934b..eb15b890b1 100644 --- a/hw/macio.c +++ b/hw/macio.c @@ -97,12 +97,12 @@ static TypeInfo macio_info = { .class_init = macio_class_init, }; -static void macio_register(void) +static void macio_register_types(void) { type_register_static(&macio_info); } -device_init(macio_register); +type_init(macio_register_types) void macio_init (PCIBus *bus, int device_id, int is_oldworld, MemoryRegion *pic_mem, MemoryRegion *dbdma_mem, diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index b628f1718d..f6f1937442 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -295,9 +295,9 @@ static TypeInfo mv88w8618_audio_info = { .class_init = mv88w8618_audio_class_init, }; -static void mv88w8618_register_devices(void) +static void mv88w8618_register_types(void) { type_register_static(&mv88w8618_audio_info); } -device_init(mv88w8618_register_devices) +type_init(mv88w8618_register_types) diff --git a/hw/max111x.c b/hw/max111x.c index 9d61aa98db..706d89f4fd 100644 --- a/hw/max111x.c +++ b/hw/max111x.c @@ -183,10 +183,10 @@ static TypeInfo max1111_info = { .class_init = max1111_class_init, }; -static void max111x_register_devices(void) +static void max111x_register_types(void) { type_register_static(&max1110_info); type_register_static(&max1111_info); } -device_init(max111x_register_devices) +type_init(max111x_register_types) diff --git a/hw/max7310.c b/hw/max7310.c index 3a6bb961ef..1ed18ba876 100644 --- a/hw/max7310.c +++ b/hw/max7310.c @@ -205,9 +205,9 @@ static TypeInfo max7310_info = { .class_init = max7310_class_init, }; -static void max7310_register_devices(void) +static void max7310_register_types(void) { type_register_static(&max7310_info); } -device_init(max7310_register_devices) +type_init(max7310_register_types) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 4a43225707..8b5cf8c81f 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -25,10 +25,13 @@ #include "qemu-timer.h" #include "sysemu.h" #include "pc.h" -#include "apic.h" #include "isa.h" #include "mc146818rtc.h" +#ifdef TARGET_I386 +#include "apic.h" +#endif + //#define DEBUG_CMOS //#define DEBUG_COALESCED @@ -102,6 +105,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 +440,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 +601,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 +689,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); @@ -733,8 +749,9 @@ static TypeInfo mc146818rtc_info = { .class_init = rtc_class_initfn, }; -static void mc146818rtc_register(void) +static void mc146818rtc_register_types(void) { type_register_static(&mc146818rtc_info); } -device_init(mc146818rtc_register) + +type_init(mc146818rtc_register_types) diff --git a/hw/milkymist-ac97.c b/hw/milkymist-ac97.c index 0881643a4c..4414f39734 100644 --- a/hw/milkymist-ac97.c +++ b/hw/milkymist-ac97.c @@ -336,9 +336,9 @@ static TypeInfo milkymist_ac97_info = { .class_init = milkymist_ac97_class_init, }; -static void milkymist_ac97_register(void) +static void milkymist_ac97_register_types(void) { type_register_static(&milkymist_ac97_info); } -device_init(milkymist_ac97_register) +type_init(milkymist_ac97_register_types) diff --git a/hw/milkymist-hpdmc.c b/hw/milkymist-hpdmc.c index b5122afd57..2da0293683 100644 --- a/hw/milkymist-hpdmc.c +++ b/hw/milkymist-hpdmc.c @@ -162,9 +162,9 @@ static TypeInfo milkymist_hpdmc_info = { .class_init = milkymist_hpdmc_class_init, }; -static void milkymist_hpdmc_register(void) +static void milkymist_hpdmc_register_types(void) { type_register_static(&milkymist_hpdmc_info); } -device_init(milkymist_hpdmc_register) +type_init(milkymist_hpdmc_register_types) diff --git a/hw/milkymist-memcard.c b/hw/milkymist-memcard.c index 3c1c68a2e7..3515c3cd9a 100644 --- a/hw/milkymist-memcard.c +++ b/hw/milkymist-memcard.c @@ -295,9 +295,9 @@ static TypeInfo milkymist_memcard_info = { .class_init = milkymist_memcard_class_init, }; -static void milkymist_memcard_register(void) +static void milkymist_memcard_register_types(void) { type_register_static(&milkymist_memcard_info); } -device_init(milkymist_memcard_register) +type_init(milkymist_memcard_register_types) diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c index b9b553fc95..70bf336add 100644 --- a/hw/milkymist-minimac2.c +++ b/hw/milkymist-minimac2.c @@ -542,9 +542,9 @@ static TypeInfo milkymist_minimac2_info = { .class_init = milkymist_minimac2_class_init, }; -static void milkymist_minimac2_register(void) +static void milkymist_minimac2_register_types(void) { type_register_static(&milkymist_minimac2_info); } -device_init(milkymist_minimac2_register) +type_init(milkymist_minimac2_register_types) diff --git a/hw/milkymist-pfpu.c b/hw/milkymist-pfpu.c index 1b73a4686b..0f9ff4a13d 100644 --- a/hw/milkymist-pfpu.c +++ b/hw/milkymist-pfpu.c @@ -536,9 +536,9 @@ static TypeInfo milkymist_pfpu_info = { .class_init = milkymist_pfpu_class_init, }; -static void milkymist_pfpu_register(void) +static void milkymist_pfpu_register_types(void) { type_register_static(&milkymist_pfpu_info); } -device_init(milkymist_pfpu_register) +type_init(milkymist_pfpu_register_types) diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index 5d496cbdb0..ecc2be9225 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -323,9 +323,9 @@ static TypeInfo milkymist_softusb_info = { .class_init = milkymist_softusb_class_init, }; -static void milkymist_softusb_register(void) +static void milkymist_softusb_register_types(void) { type_register_static(&milkymist_softusb_info); } -device_init(milkymist_softusb_register) +type_init(milkymist_softusb_register_types) diff --git a/hw/milkymist-sysctl.c b/hw/milkymist-sysctl.c index 18171f6165..a88548e0aa 100644 --- a/hw/milkymist-sysctl.c +++ b/hw/milkymist-sysctl.c @@ -322,9 +322,9 @@ static TypeInfo milkymist_sysctl_info = { .class_init = milkymist_sysctl_class_init, }; -static void milkymist_sysctl_register(void) +static void milkymist_sysctl_register_types(void) { type_register_static(&milkymist_sysctl_info); } -device_init(milkymist_sysctl_register) +type_init(milkymist_sysctl_register_types) diff --git a/hw/milkymist-tmu2.c b/hw/milkymist-tmu2.c index 474eae0a4a..210ceede2a 100644 --- a/hw/milkymist-tmu2.c +++ b/hw/milkymist-tmu2.c @@ -482,9 +482,9 @@ static TypeInfo milkymist_tmu2_info = { .class_init = milkymist_tmu2_class_init, }; -static void milkymist_tmu2_register(void) +static void milkymist_tmu2_register_types(void) { type_register_static(&milkymist_tmu2_info); } -device_init(milkymist_tmu2_register) +type_init(milkymist_tmu2_register_types) diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c index f9a229cf68..291fe3c57b 100644 --- a/hw/milkymist-uart.c +++ b/hw/milkymist-uart.c @@ -235,9 +235,9 @@ static TypeInfo milkymist_uart_info = { .class_init = milkymist_uart_class_init, }; -static void milkymist_uart_register(void) +static void milkymist_uart_register_types(void) { type_register_static(&milkymist_uart_info); } -device_init(milkymist_uart_register) +type_init(milkymist_uart_register_types) diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c index 92ad02f9e2..69afd72d8a 100644 --- a/hw/milkymist-vgafb.c +++ b/hw/milkymist-vgafb.c @@ -323,9 +323,9 @@ static TypeInfo milkymist_vgafb_info = { .class_init = milkymist_vgafb_class_init, }; -static void milkymist_vgafb_register(void) +static void milkymist_vgafb_register_types(void) { type_register_static(&milkymist_vgafb_info); } -device_init(milkymist_vgafb_register) +type_init(milkymist_vgafb_register_types) @@ -10,9 +10,6 @@ PCIBus *gt64120_register(qemu_irq *pic); /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); -/* jazz_led.c */ -void jazz_led_init(MemoryRegion *address_space, target_phys_addr_t base); - /* rc4030.c */ typedef struct rc4030DMAState *rc4030_dma; void rc4030_dma_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write); diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c index 163a668533..e3ba9dd42d 100644 --- a/hw/mips_fulong2e.c +++ b/hw/mips_fulong2e.c @@ -40,6 +40,7 @@ #include "elf.h" #include "vt82c686.h" #include "mc146818rtc.h" +#include "i8254.h" #include "blockdev.h" #include "exec-memory.h" @@ -363,7 +364,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); /* init other devices */ - pit = pit_init(isa_bus, 0x40, 0); + pit = pit_init(isa_bus, 0x40, 0, NULL); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index 63165b9a38..2b4678e170 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -36,6 +36,8 @@ #include "mips-bios.h" #include "loader.h" #include "mc146818rtc.h" +#include "i8254.h" +#include "pcspk.h" #include "blockdev.h" #include "sysbus.h" #include "exec-memory.h" @@ -191,8 +193,8 @@ static void mips_jazz_init(MemoryRegion *address_space, isa_bus_irqs(isa_bus, i8259); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); - pit = pit_init(isa_bus, 0x40, 0); - pcspk_init(pit); + pit = pit_init(isa_bus, 0x40, 0, NULL); + pcspk_init(isa_bus, pit); /* ISA IO space at 0x90000000 */ isa_mmio_init(0x90000000, 0x01000000); @@ -293,7 +295,7 @@ static void mips_jazz_init(MemoryRegion *address_space, sysbus_mmio_map(sysbus, 0, 0x80009000); /* LED indicator */ - jazz_led_init(address_space, 0x8000f000); + sysbus_create_simple("jazz-led", 0x8000f000, NULL); } static diff --git a/hw/mips_malta.c b/hw/mips_malta.c index d232630e66..b1563ed2a7 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -45,6 +45,7 @@ #include "loader.h" #include "elf.h" #include "mc146818rtc.h" +#include "i8254.h" #include "blockdev.h" #include "exec-memory.h" #include "sysbus.h" /* SysBusDevice */ @@ -966,10 +967,10 @@ 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); + pit = pit_init(isa_bus, 0x40, 0, NULL); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); @@ -1029,7 +1030,7 @@ static QEMUMachine mips_malta_machine = { .is_default = 1, }; -static void mips_malta_device_init(void) +static void mips_malta_register_types(void) { type_register_static(&mips_malta_device); } @@ -1039,5 +1040,5 @@ static void mips_malta_machine_init(void) qemu_register_machine(&mips_malta_machine); } -device_init(mips_malta_device_init); +type_init(mips_malta_register_types) machine_init(mips_malta_machine_init); diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index 1c0615c1da..83401f0648 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -22,6 +22,7 @@ #include "loader.h" #include "elf.h" #include "mc146818rtc.h" +#include "i8254.h" #include "blockdev.h" #include "exec-memory.h" @@ -269,7 +270,7 @@ void mips_r4k_init (ram_addr_t ram_size, isa_mmio_init(0x14000000, 0x00010000); isa_mem_base = 0x10000000; - pit = pit_init(isa_bus, 0x40, 0); + pit = pit_init(isa_bus, 0x40, 0, NULL); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { diff --git a/hw/mipsnet.c b/hw/mipsnet.c index a0e6c9fa1e..50d92f8f54 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -276,9 +276,9 @@ static TypeInfo mipsnet_info = { .class_init = mipsnet_class_init, }; -static void mipsnet_register_devices(void) +static void mipsnet_register_types(void) { type_register_static(&mipsnet_info); } -device_init(mipsnet_register_devices) +type_init(mipsnet_register_types) diff --git a/hw/mpc8544_guts.c b/hw/mpc8544_guts.c index 28cd60d89c..aeb2de7ccc 100644 --- a/hw/mpc8544_guts.c +++ b/hw/mpc8544_guts.c @@ -135,8 +135,9 @@ static TypeInfo mpc8544_guts_info = { .class_init = mpc8544_guts_class_init, }; -static void mpc8544_guts_register(void) +static void mpc8544_guts_register_types(void) { type_register_static(&mpc8544_guts_info); } -device_init(mpc8544_guts_register); + +type_init(mpc8544_guts_register_types) diff --git a/hw/msmouse.c b/hw/msmouse.c index c3b57ea31c..9c492a4637 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 8b853b35bf..456cb21424 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/mst_fpga.c b/hw/mst_fpga.c index 1729db01a4..024192d135 100644 --- a/hw/mst_fpga.c +++ b/hw/mst_fpga.c @@ -255,8 +255,9 @@ static TypeInfo mst_fpga_info = { .class_init = mst_fpga_class_init, }; -static void mst_fpga_register(void) +static void mst_fpga_register_types(void) { type_register_static(&mst_fpga_info); } -device_init(mst_fpga_register); + +type_init(mst_fpga_register_types) diff --git a/hw/musicpal.c b/hw/musicpal.c index ac909248d4..187a1aef5e 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -1681,7 +1681,7 @@ static TypeInfo mv88w8618_wlan_info = { .class_init = mv88w8618_wlan_class_init, }; -static void musicpal_register_devices(void) +static void musicpal_register_types(void) { type_register_static(&mv88w8618_pic_info); type_register_static(&mv88w8618_pit_info); @@ -1693,4 +1693,4 @@ static void musicpal_register_devices(void) type_register_static(&musicpal_key_info); } -device_init(musicpal_register_devices) +type_init(musicpal_register_types) @@ -442,7 +442,7 @@ static TypeInfo nand_info = { .class_init = nand_class_init, }; -static void nand_create_device(void) +static void nand_register_types(void) { type_register_static(&nand_info); } @@ -635,7 +635,7 @@ DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) return dev; } -device_init(nand_create_device) +type_init(nand_register_types) #else diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c index 1352282152..a4a783ab89 100644 --- a/hw/ne2000-isa.c +++ b/hw/ne2000-isa.c @@ -104,9 +104,9 @@ static TypeInfo ne2000_isa_info = { .class_init = isa_ne2000_class_initfn, }; -static void ne2000_isa_register_devices(void) +static void ne2000_isa_register_types(void) { type_register_static(&ne2000_isa_info); } -device_init(ne2000_isa_register_devices) +type_init(ne2000_isa_register_types) diff --git a/hw/ne2000.c b/hw/ne2000.c index 080811ec4d..71452e1f2e 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; @@ -812,9 +805,9 @@ static TypeInfo ne2000_info = { .class_init = ne2000_class_init, }; -static void ne2000_register_devices(void) +static void ne2000_register_types(void) { type_register_static(&ne2000_info); } -device_init(ne2000_register_devices) +type_init(ne2000_register_types) diff --git a/hw/nseries.c b/hw/nseries.c index d429dbdebf..c5b31843dd 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -204,6 +204,8 @@ static void n8x0_i2c_setup(struct n800_s *s) qdev_get_gpio_in(s->cpu->ih[0], OMAP_INT_24XX_SYS_NIRQ)); + qemu_system_powerdown = qdev_get_gpio_in(dev, 3); + /* Attach a TMP105 PM chip (A0 wired to ground) */ dev = i2c_create_slave(s->i2c, "tmp105", N8X0_TMP105_ADDR); qdev_connect_gpio_out(dev, 0, tmp_irq); diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c index 9a9a8e183f..201ff77c36 100644 --- a/hw/omap_gpio.c +++ b/hw/omap_gpio.c @@ -783,10 +783,10 @@ static TypeInfo omap2_gpio_info = { .class_init = omap2_gpio_class_init, }; -static void omap_gpio_register_device(void) +static void omap_gpio_register_types(void) { type_register_static(&omap_gpio_info); type_register_static(&omap2_gpio_info); } -device_init(omap_gpio_register_device) +type_init(omap_gpio_register_types) diff --git a/hw/omap_intc.c b/hw/omap_intc.c index 5aa98a8fdb..5076e07ed5 100644 --- a/hw/omap_intc.c +++ b/hw/omap_intc.c @@ -640,10 +640,10 @@ static TypeInfo omap2_intc_info = { .class_init = omap2_intc_class_init, }; -static void omap_intc_register_device(void) +static void omap_intc_register_types(void) { type_register_static(&omap_intc_info); type_register_static(&omap2_intc_info); } -device_init(omap_intc_register_device) +type_init(omap_intc_register_types) diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index f265306556..f172093876 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/onenand.c b/hw/onenand.c index 8744b04db3..db6af682c4 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -828,7 +828,7 @@ static TypeInfo onenand_info = { .class_init = onenand_class_init, }; -static void onenand_register_device(void) +static void onenand_register_types(void) { type_register_static(&onenand_info); } @@ -838,4 +838,4 @@ void *onenand_raw_otp(DeviceState *onenand_device) return FROM_SYSBUS(OneNANDState, sysbus_from_qdev(onenand_device))->otp; } -device_init(onenand_register_device) +type_init(onenand_register_types) diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c index 09f2757df3..9b036cb103 100644 --- a/hw/opencores_eth.c +++ b/hw/opencores_eth.c @@ -750,9 +750,9 @@ static TypeInfo open_eth_info = { .class_init = open_eth_class_init, }; -static void open_eth_register_devices(void) +static void open_eth_register_types(void) { type_register_static(&open_eth_info); } -device_init(open_eth_register_devices) +type_init(open_eth_register_types) diff --git a/hw/parallel.c b/hw/parallel.c index 484d727927..219f38436f 100644 --- a/hw/parallel.c +++ b/hw/parallel.c @@ -606,9 +606,9 @@ static TypeInfo parallel_isa_info = { .class_init = parallel_isa_class_initfn, }; -static void parallel_register_devices(void) +static void parallel_register_types(void) { type_register_static(¶llel_isa_info); } -device_init(parallel_register_devices) +type_init(parallel_register_types) @@ -36,6 +36,8 @@ #include "elf.h" #include "multiboot.h" #include "mc146818rtc.h" +#include "i8254.h" +#include "pcspk.h" #include "msi.h" #include "sysbus.h" #include "sysemu.h" @@ -58,10 +60,6 @@ #define DPRINTF(fmt, ...) #endif -#define BIOS_FILENAME "bios.bin" - -#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024) - /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */ #define ACPI_DATA_SIZE 0x10000 #define BIOS_CFG_IOPORT 0x510 @@ -337,6 +335,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, { int val, nb, nb_heads, max_track, last_sect, i; FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; + FDriveRate rate; BlockDriverState *fd[MAX_FD]; static pc_cmos_init_late_arg arg; @@ -385,7 +384,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, if (fd[i] && bdrv_is_inserted(fd[i])) { bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track, &last_sect, FDRIVE_DRV_NONE, - &fd_type[i]); + &fd_type[i], &rate); } } } @@ -514,11 +513,12 @@ static TypeInfo port92_info = { .class_init = port92_class_initfn, }; -static void port92_register(void) +static void port92_register_types(void) { type_register_static(&port92_info); } -device_init(port92_register) + +type_init(port92_register_types) static void handle_a20_line_change(void *opaque, int irq, int level) { @@ -889,7 +889,7 @@ static DeviceState *apic_init(void *env, uint8_t apic_id) DeviceState *dev; static int apic_mapped; - if (kvm_enabled() && kvm_irqchip_in_kernel()) { + if (kvm_irqchip_in_kernel()) { dev = qdev_create(NULL, "kvm-apic"); } else { dev = qdev_create(NULL, "apic"); @@ -908,24 +908,13 @@ static DeviceState *apic_init(void *env, uint8_t apic_id) } /* KVM does not support MSI yet. */ - if (!kvm_enabled() || !kvm_irqchip_in_kernel()) { + if (!kvm_irqchip_in_kernel()) { msi_supported = true; } 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; @@ -987,11 +976,9 @@ void pc_memory_init(MemoryRegion *system_memory, MemoryRegion *rom_memory, MemoryRegion **ram_memory) { - char *filename; - int ret, linux_boot, i; - MemoryRegion *ram, *bios, *isa_bios, *option_rom_mr; + int linux_boot, i; + MemoryRegion *ram, *option_rom_mr; MemoryRegion *ram_below_4g, *ram_above_4g; - int bios_size, isa_bios_size; void *fw_cfg; linux_boot = (kernel_filename != NULL); @@ -1017,44 +1004,9 @@ void pc_memory_init(MemoryRegion *system_memory, ram_above_4g); } - /* BIOS load */ - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - bios = g_malloc(sizeof(*bios)); - memory_region_init_ram(bios, "pc.bios", bios_size); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); - } - if (filename) { - g_free(filename); - } - /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = bios_size; - if (isa_bios_size > (128 * 1024)) - isa_bios_size = 128 * 1024; - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_alias(isa_bios, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - isa_bios, - 1); - memory_region_set_readonly(isa_bios, true); + + /* Initialize PC system firmware */ + pc_system_firmware_init(rom_memory); option_rom_mr = g_malloc(sizeof(*option_rom_mr)); memory_region_init_ram(option_rom_mr, "pc.rom", PC_ROM_SIZE); @@ -1064,11 +1016,6 @@ void pc_memory_init(MemoryRegion *system_memory, option_rom_mr, 1); - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - bios); - fw_cfg = bochs_bios_init(); rom_set_fw(fw_cfg); @@ -1137,6 +1084,9 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, { int i; DriveInfo *fd[MAX_FD]; + DeviceState *hpet = NULL; + int pit_isa_irq = 0; + qemu_irq pit_alt_irq = NULL; qemu_irq rtc_irq = NULL; qemu_irq *a20_line; ISADevice *i8042, *port92, *vmmouse, *pit; @@ -1147,21 +1097,27 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); if (!no_hpet) { - DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); + hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); if (hpet) { for (i = 0; i < GSI_NUM_PINS; i++) { sysbus_connect_irq(sysbus_from_qdev(hpet), i, gsi[i]); } - rtc_irq = qdev_get_gpio_in(hpet, 0); + pit_isa_irq = -1; + pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); + rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT); } } *rtc_state = rtc_init(isa_bus, 2000, rtc_irq); qemu_register_boot_set(pc_boot_set, *rtc_state); - pit = pit_init(isa_bus, 0x40, 0); - pcspk_init(pit); + pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); + if (hpet) { + /* connect PIT to output control line of the HPET */ + qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(&pit->qdev, 0)); + } + pcspk_init(isa_bus, pit); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { @@ -81,31 +81,6 @@ typedef struct GSIState { void gsi_handler(void *opaque, int n, int level); -/* i8254.c */ - -#define PIT_FREQ 1193182 - -static inline ISADevice *pit_init(ISABus *bus, int base, int irq) -{ - ISADevice *dev; - - dev = isa_create(bus, "isa-pit"); - qdev_prop_set_uint32(&dev->qdev, "iobase", base); - qdev_prop_set_uint32(&dev->qdev, "irq", irq); - qdev_init_nofail(&dev->qdev); - - return dev; -} - -void pit_set_gate(ISADevice *dev, int channel, int val); -int pit_get_gate(ISADevice *dev, int channel); -int pit_get_initial_count(ISADevice *dev, int channel); -int pit_get_mode(ISADevice *dev, int channel); -int pit_get_out(ISADevice *dev, int channel, int64_t current_time); - -void hpet_pit_disable(void); -void hpet_pit_enable(void); - /* vmport.c */ static inline void vmport_init(ISABus *bus) { @@ -128,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); @@ -167,17 +141,13 @@ 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); /* hpet.c */ extern int no_hpet; -/* pcspk.c */ -void pcspk_init(ISADevice *pit); -int pcspk_audio_init(ISABus *bus); - /* piix_pci.c */ struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; @@ -245,6 +215,9 @@ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd) return true; } +/* pc_sysfw.c */ +void pc_system_firmware_init(MemoryRegion *rom_memory); + /* e820 types */ #define E820_RAM 1 #define E820_RESERVED 2 diff --git a/hw/pc_piix.c b/hw/pc_piix.c index c06f1b544e..6c5c40f5df 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -104,7 +104,7 @@ static void ioapic_init(GSIState *gsi_state) SysBusDevice *d; unsigned int i; - if (kvm_enabled() && kvm_irqchip_in_kernel()) { + if (kvm_irqchip_in_kernel()) { dev = qdev_create(NULL, "kvm-ioapic"); } else { dev = qdev_create(NULL, "ioapic"); @@ -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]; @@ -183,7 +182,7 @@ static void pc_init1(MemoryRegion *system_memory, } gsi_state = g_malloc0(sizeof(*gsi_state)); - if (kvm_enabled() && kvm_irqchip_in_kernel()) { + if (kvm_irqchip_in_kernel()) { kvm_piix3_setup_irq_routing(pci_enabled); gsi = qemu_allocate_irqs(kvm_piix3_gsi_handler, gsi_state, GSI_NUM_PINS); @@ -209,7 +208,7 @@ static void pc_init1(MemoryRegion *system_memory, } isa_bus_irqs(isa_bus, gsi); - if (kvm_enabled() && kvm_irqchip_in_kernel()) { + if (kvm_irqchip_in_kernel()) { i8259 = kvm_i8259_init(isa_bus); } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); @@ -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); } @@ -371,8 +365,8 @@ static void pc_xen_hvm_init(ram_addr_t ram_size, } #endif -static QEMUMachine pc_machine_v1_0 = { - .name = "pc-1.0", +static QEMUMachine pc_machine_v1_1 = { + .name = "pc-1.1", .alias = "pc", .desc = "Standard PC", .init = pc_init_pci, @@ -380,12 +374,42 @@ static QEMUMachine pc_machine_v1_0 = { .is_default = 1, }; +static QEMUMachine pc_machine_v1_0 = { + .name = "pc-1.0", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), + }, { + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { /* end of list */ } + }, +}; + static QEMUMachine pc_machine_v0_15 = { .name = "pc-0.15", .desc = "Standard PC", .init = pc_init_pci, .max_cpus = 255, - .is_default = 1, + .compat_props = (GlobalProperty[]) { + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), + }, { + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { /* end of list */ } + }, }; static QEMUMachine pc_machine_v0_14 = { @@ -418,6 +442,15 @@ static QEMUMachine pc_machine_v0_14 = { .driver = "virtio-balloon-pci", .property = "event_idx", .value = "off", + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), }, { /* end of list */ } }, @@ -465,6 +498,15 @@ static QEMUMachine pc_machine_v0_13 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), }, { /* end of list */ } }, @@ -516,6 +558,15 @@ static QEMUMachine pc_machine_v0_12 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), }, { /* end of list */ } } @@ -575,6 +626,15 @@ static QEMUMachine pc_machine_v0_11 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), }, { /* end of list */ } } @@ -646,6 +706,15 @@ static QEMUMachine pc_machine_v0_10 = { .driver = "AC97", .property = "use_broken_id", .value = stringify(1), + },{ + .driver = "isa-fdc", + .property = "check_media_rate", + .value = "off", + }, + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), }, { /* end of list */ } }, @@ -656,6 +725,14 @@ static QEMUMachine isapc_machine = { .desc = "ISA-only PC", .init = pc_init_isa, .max_cpus = 1, + .compat_props = (GlobalProperty[]) { + { + .driver = "pc-sysfw", + .property = "rom_only", + .value = stringify(1), + }, + { /* end of list */ } + }, }; #ifdef CONFIG_XEN @@ -670,6 +747,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { + qemu_register_machine(&pc_machine_v1_1); qemu_register_machine(&pc_machine_v1_0); qemu_register_machine(&pc_machine_v0_15); qemu_register_machine(&pc_machine_v0_14); diff --git a/hw/pc_sysfw.c b/hw/pc_sysfw.c new file mode 100644 index 0000000000..abf9004182 --- /dev/null +++ b/hw/pc_sysfw.c @@ -0,0 +1,254 @@ +/* + * QEMU PC System Firmware + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2011-2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "hw.h" +#include "pc.h" +#include "hw/boards.h" +#include "loader.h" +#include "sysemu.h" +#include "flash.h" +#include "kvm.h" + +#define BIOS_FILENAME "bios.bin" + +typedef struct PcSysFwDevice { + SysBusDevice busdev; + uint8_t rom_only; +} PcSysFwDevice; + +static void pc_isa_bios_init(MemoryRegion *rom_memory, + MemoryRegion *flash_mem, + int ram_size) +{ + int isa_bios_size; + MemoryRegion *isa_bios; + uint64_t flash_size; + void *flash_ptr, *isa_bios_ptr; + + flash_size = memory_region_size(flash_mem); + + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = flash_size; + if (isa_bios_size > (128 * 1024)) { + isa_bios_size = 128 * 1024; + } + isa_bios = g_malloc(sizeof(*isa_bios)); + memory_region_init_ram(isa_bios, "isa-bios", isa_bios_size); + vmstate_register_ram_global(isa_bios); + memory_region_add_subregion_overlap(rom_memory, + 0x100000 - isa_bios_size, + isa_bios, + 1); + + /* copy ISA rom image from top of flash memory */ + flash_ptr = memory_region_get_ram_ptr(flash_mem); + isa_bios_ptr = memory_region_get_ram_ptr(isa_bios); + memcpy(isa_bios_ptr, + ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size), + isa_bios_size); + + memory_region_set_readonly(isa_bios, true); +} + +static void pc_fw_add_pflash_drv(void) +{ + QemuOpts *opts; + QEMUMachine *machine; + char *filename; + + if (bios_name == NULL) { + bios_name = BIOS_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + opts = drive_add(IF_PFLASH, -1, filename, "readonly=on"); + if (opts == NULL) { + return; + } + + machine = find_default_machine(); + if (machine == NULL) { + return; + } + + drive_init(opts, machine->use_scsi); +} + +static void pc_system_flash_init(MemoryRegion *rom_memory, + DriveInfo *pflash_drv) +{ + BlockDriverState *bdrv; + int64_t size; + target_phys_addr_t phys_addr; + int sector_bits, sector_size; + pflash_t *system_flash; + MemoryRegion *flash_mem; + + bdrv = pflash_drv->bdrv; + size = bdrv_getlength(pflash_drv->bdrv); + sector_bits = 12; + sector_size = 1 << sector_bits; + + if ((size % sector_size) != 0) { + fprintf(stderr, + "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n", + sector_size); + exit(1); + } + + phys_addr = 0x100000000ULL - size; + system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size, + bdrv, sector_size, size >> sector_bits, + 1, 0x0000, 0x0000, 0x0000, 0x0000, 0); + flash_mem = pflash_cfi01_get_memory(system_flash); + + pc_isa_bios_init(rom_memory, flash_mem, size); +} + +static void old_pc_system_rom_init(MemoryRegion *rom_memory) +{ + char *filename; + MemoryRegion *bios, *isa_bios; + int bios_size, isa_bios_size; + int ret; + + /* BIOS load */ + if (bios_name == NULL) { + bios_name = BIOS_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + if (bios_size <= 0 || + (bios_size % 65536) != 0) { + goto bios_error; + } + bios = g_malloc(sizeof(*bios)); + memory_region_init_ram(bios, "pc.bios", bios_size); + vmstate_register_ram_global(bios); + memory_region_set_readonly(bios, true); + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); + } + if (filename) { + g_free(filename); + } + + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = bios_size; + if (isa_bios_size > (128 * 1024)) { + isa_bios_size = 128 * 1024; + } + isa_bios = g_malloc(sizeof(*isa_bios)); + memory_region_init_alias(isa_bios, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(rom_memory, + 0x100000 - isa_bios_size, + isa_bios, + 1); + memory_region_set_readonly(isa_bios, true); + + /* map all the bios at the top of memory */ + memory_region_add_subregion(rom_memory, + (uint32_t)(-bios_size), + bios); +} + +void pc_system_firmware_init(MemoryRegion *rom_memory) +{ + DriveInfo *pflash_drv; + PcSysFwDevice *sysfw_dev; + + sysfw_dev = (PcSysFwDevice*) qdev_create(NULL, "pc-sysfw"); + + if (sysfw_dev->rom_only) { + old_pc_system_rom_init(rom_memory); + return; + } + + pflash_drv = drive_get(IF_PFLASH, 0, 0); + + /* Currently KVM cannot execute from device memory. + Use old rom based firmware initialization for KVM. */ + if (kvm_enabled()) { + if (pflash_drv != NULL) { + fprintf(stderr, "qemu: pflash cannot be used with kvm enabled\n"); + exit(1); + } else { + sysfw_dev->rom_only = 1; + old_pc_system_rom_init(rom_memory); + return; + } + } + + /* If a pflash drive is not found, then create one using + the bios filename. */ + if (pflash_drv == NULL) { + pc_fw_add_pflash_drv(); + pflash_drv = drive_get(IF_PFLASH, 0, 0); + } + + if (pflash_drv != NULL) { + pc_system_flash_init(rom_memory, pflash_drv); + } else { + fprintf(stderr, "qemu: PC system firmware (pflash) not available\n"); + exit(1); + } +} + +static Property pcsysfw_properties[] = { + DEFINE_PROP_UINT8("rom_only", PcSysFwDevice, rom_only, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pcsysfw_class_init (ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS (klass); + + dc->desc = "PC System Firmware"; + dc->props = pcsysfw_properties; +} + +static TypeInfo pcsysfw_info = { + .name = "pc-sysfw", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof (PcSysFwDevice), + .class_init = pcsysfw_class_init, +}; + +static void pcsysfw_register (void) +{ + type_register_static (&pcsysfw_info); +} + +type_init (pcsysfw_register); + @@ -614,7 +614,7 @@ static void pci_init_w1cmask(PCIDevice *dev) PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); } -static void pci_init_wmask_bridge(PCIDevice *d) +static void pci_init_mask_bridge(PCIDevice *d) { /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and PCI_SEC_LETENCY_TIMER */ @@ -635,6 +635,14 @@ static void pci_init_wmask_bridge(PCIDevice *d) /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */ memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); + /* Supported memory and i/o types */ + d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_32; + d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_32; + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_TYPE_64); + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_TYPE_64); + /* TODO: add this define to pci_regs.h in linux and then in qemu. */ #define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ #define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ @@ -657,6 +665,9 @@ static void pci_init_wmask_bridge(PCIDevice *d) * completeness. */ pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_DISCARD_STATUS); + d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; + pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_TYPE_MASK); } static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) @@ -778,7 +789,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_init_wmask(pci_dev); pci_init_w1cmask(pci_dev); if (pc->is_bridge) { - pci_init_wmask_bridge(pci_dev); + pci_init_mask_bridge(pci_dev); } if (pci_init_multifunction(bus, pci_dev)) { pci_config_free(pci_dev); @@ -2003,9 +2014,9 @@ static TypeInfo pci_device_type_info = { .class_init = pci_device_class_init, }; -static void pci_register_devices(void) +static void pci_register_types(void) { type_register_static(&pci_device_type_info); } -device_init(pci_register_devices); +type_init(pci_register_types) @@ -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 @@ -464,6 +465,67 @@ pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask) return val & mask; } +/* Access a register specified by a mask */ +static inline void +pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg) +{ + uint8_t val = pci_get_byte(config); + uint8_t rval = reg << (ffs(mask) - 1); + pci_set_byte(config, (~mask & val) | (mask & rval)); +} + +static inline uint8_t +pci_get_byte_by_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg) +{ + uint16_t val = pci_get_word(config); + uint16_t rval = reg << (ffs(mask) - 1); + pci_set_word(config, (~mask & val) | (mask & rval)); +} + +static inline uint16_t +pci_get_word_by_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg) +{ + uint32_t val = pci_get_long(config); + uint32_t rval = reg << (ffs(mask) - 1); + pci_set_long(config, (~mask & val) | (mask & rval)); +} + +static inline uint32_t +pci_get_long_by_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) +{ + uint64_t val = pci_get_quad(config); + uint64_t rval = reg << (ffs(mask) - 1); + pci_set_quad(config, (~mask & val) | (mask & rval)); +} + +static inline uint64_t +pci_get_quad_by_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + return (val & mask) >> (ffs(mask) - 1); +} + PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, const char *name); PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, diff --git a/hw/pckbd.c b/hw/pckbd.c index b4c53bed6c..69857bade9 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -513,8 +513,9 @@ static TypeInfo i8042_info = { .class_init = i8042_class_initfn, }; -static void i8042_register(void) +static void i8042_register_types(void) { type_register_static(&i8042_info); } -device_init(i8042_register) + +type_init(i8042_register_types) diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index 439f32caae..34d73aaea1 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; @@ -376,9 +369,9 @@ static TypeInfo pcnet_info = { .class_init = pcnet_class_init, }; -static void pci_pcnet_register_devices(void) +static void pci_pcnet_register_types(void) { type_register_static(&pcnet_info); } -device_init(pci_pcnet_register_devices) +type_init(pci_pcnet_register_types) diff --git a/hw/pcspk.c b/hw/pcspk.c index acb016773b..e4303247d4 100644 --- a/hw/pcspk.c +++ b/hw/pcspk.c @@ -27,6 +27,8 @@ #include "isa.h" #include "audio/audio.h" #include "qemu-timer.h" +#include "i8254.h" +#include "pcspk.h" #define PCSPK_BUF_LEN 1792 #define PCSPK_SAMPLE_RATE 32000 @@ -34,10 +36,13 @@ #define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) typedef struct { + ISADevice dev; + MemoryRegion ioport; + uint32_t iobase; uint8_t sample_buf[PCSPK_BUF_LEN]; QEMUSoundCard card; SWVoiceOut *voice; - ISADevice *pit; + void *pit; unsigned int pit_count; unsigned int samples; unsigned int play_pos; @@ -46,7 +51,7 @@ typedef struct { } PCSpkState; static const char *s_spk = "pcspk"; -static PCSpkState pcspk_state; +static PCSpkState *pcspk_state; static inline void generate_samples(PCSpkState *s) { @@ -70,12 +75,16 @@ static inline void generate_samples(PCSpkState *s) static void pcspk_callback(void *opaque, int free) { PCSpkState *s = opaque; + PITChannelInfo ch; unsigned int n; - if (pit_get_mode(s->pit, 2) != 3) + pit_get_channel_info(s->pit, 2, &ch); + + if (ch.mode != 3) { return; + } - n = pit_get_initial_count(s->pit, 2); + n = ch.initial_count; /* avoid frequencies that are not reproducible with sample rate */ if (n < PCSPK_MIN_COUNT) n = 0; @@ -98,7 +107,7 @@ static void pcspk_callback(void *opaque, int free) int pcspk_audio_init(ISABus *bus) { - PCSpkState *s = &pcspk_state; + PCSpkState *s = pcspk_state; struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0}; AUD_register_card(s_spk, &s->card); @@ -112,18 +121,22 @@ int pcspk_audio_init(ISABus *bus) return 0; } -static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) +static uint64_t pcspk_io_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PCSpkState *s = opaque; - int out; + PITChannelInfo ch; + + pit_get_channel_info(s->pit, 2, &ch); s->dummy_refresh_clock ^= (1 << 4); - out = pit_get_out(s->pit, 2, qemu_get_clock_ns(vm_clock)) << 5; - return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out; + return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock | + (ch.out << 5); } -static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) +static void pcspk_io_write(void *opaque, target_phys_addr_t addr, uint64_t val, + unsigned size) { PCSpkState *s = opaque; const int gate = val & 1; @@ -137,11 +150,52 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } -void pcspk_init(ISADevice *pit) +static const MemoryRegionOps pcspk_io_ops = { + .read = pcspk_io_read, + .write = pcspk_io_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static int pcspk_initfn(ISADevice *dev) +{ + PCSpkState *s = DO_UPCAST(PCSpkState, dev, dev); + + memory_region_init_io(&s->ioport, &pcspk_io_ops, s, "elcr", 1); + isa_register_ioport(dev, &s->ioport, s->iobase); + + pcspk_state = s; + + return 0; +} + +static Property pcspk_properties[] = { + DEFINE_PROP_HEX32("iobase", PCSpkState, iobase, -1), + DEFINE_PROP_PTR("pit", PCSpkState, pit), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pcspk_class_initfn(ObjectClass *klass, void *data) { - PCSpkState *s = &pcspk_state; + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); - s->pit = pit; - register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s); - register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s); + ic->init = pcspk_initfn; + dc->no_user = 1; + dc->props = pcspk_properties; +} + +static TypeInfo pcspk_info = { + .name = "isa-pcspk", + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PCSpkState), + .class_init = pcspk_class_initfn, +}; + +static void pcspk_register(void) +{ + type_register_static(&pcspk_info); } +type_init(pcspk_register) diff --git a/hw/pcspk.h b/hw/pcspk.h new file mode 100644 index 0000000000..7f42bac1c8 --- /dev/null +++ b/hw/pcspk.h @@ -0,0 +1,45 @@ +/* + * QEMU PC speaker emulation + * + * Copyright (c) 2006 Joachim Henke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_PCSPK_H +#define HW_PCSPK_H + +#include "hw.h" +#include "isa.h" + +static inline ISADevice *pcspk_init(ISABus *bus, ISADevice *pit) +{ + ISADevice *dev; + + dev = isa_create(bus, "isa-pcspk"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0x61); + qdev_prop_set_ptr(&dev->qdev, "pit", pit); + qdev_init_nofail(&dev->qdev); + + return dev; +} + +int pcspk_audio_init(ISABus *bus); + +#endif /* !HW_PCSPK_H */ diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c index ee0c3baab1..b03f623cb1 100644 --- a/hw/pflash_cfi01.c +++ b/hw/pflash_cfi01.c @@ -283,8 +283,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset, TARGET_FMT_plx "\n", __func__, offset, pfl->sector_len); - memset(p + offset, 0xff, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); + if (!pfl->ro) { + memset(p + offset, 0xff, pfl->sector_len); + pflash_update(pfl, offset, pfl->sector_len); + } else { + pfl->status |= 0x20; /* Block erase error */ + } pfl->status |= 0x80; /* Ready! */ break; case 0x50: /* Clear status bits */ @@ -323,8 +327,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset, case 0x10: /* Single Byte Program */ case 0x40: /* Single Byte Program */ DPRINTF("%s: Single Byte Program\n", __func__); - pflash_data_write(pfl, offset, value, width, be); - pflash_update(pfl, offset, width); + if (!pfl->ro) { + pflash_data_write(pfl, offset, value, width, be); + pflash_update(pfl, offset, width); + } else { + pfl->status |= 0x10; /* Programming error */ + } pfl->status |= 0x80; /* Ready! */ pfl->wcycle = 0; break; @@ -372,7 +380,11 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset, case 2: switch (pfl->cmd) { case 0xe8: /* Block write */ - pflash_data_write(pfl, offset, value, width, be); + if (!pfl->ro) { + pflash_data_write(pfl, offset, value, width, be); + } else { + pfl->status |= 0x10; /* Programming error */ + } pfl->status |= 0x80; @@ -382,8 +394,12 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset, DPRINTF("%s: block write finished\n", __func__); pfl->wcycle++; - /* Flush the entire write buffer onto backing storage. */ - pflash_update(pfl, offset & mask, pfl->writeblock_size); + if (!pfl->ro) { + /* Flush the entire write buffer onto backing storage. */ + pflash_update(pfl, offset & mask, pfl->writeblock_size); + } else { + pfl->status |= 0x10; /* Programming error */ + } } pfl->counter--; @@ -607,13 +623,13 @@ pflash_t *pflash_cfi01_register(target_phys_addr_t base, } bdrv_attach_dev_nofail(pfl->bs, pfl); } -#if 0 /* XXX: there should be a bit to set up read-only, - * the same way the hardware does (with WP pin). - */ - pfl->ro = 1; -#else - pfl->ro = 0; -#endif + + if (pfl->bs) { + pfl->ro = bdrv_is_read_only(pfl->bs); + } else { + pfl->ro = 0; + } + pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl); pfl->base = base; pfl->sector_len = sector_len; diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c index 2ca0fd4560..3e2002e4b3 100644 --- a/hw/pflash_cfi02.c +++ b/hw/pflash_cfi02.c @@ -330,35 +330,37 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", __func__, offset, value, width); p = pfl->storage; - switch (width) { - case 1: - p[offset] &= value; - pflash_update(pfl, offset, 1); - break; - case 2: - if (be) { - p[offset] &= value >> 8; - p[offset + 1] &= value; - } else { + if (!pfl->ro) { + switch (width) { + case 1: p[offset] &= value; - p[offset + 1] &= value >> 8; + pflash_update(pfl, offset, 1); + break; + case 2: + if (be) { + p[offset] &= value >> 8; + p[offset + 1] &= value; + } else { + p[offset] &= value; + p[offset + 1] &= value >> 8; + } + pflash_update(pfl, offset, 2); + break; + case 4: + if (be) { + p[offset] &= value >> 24; + p[offset + 1] &= value >> 16; + p[offset + 2] &= value >> 8; + p[offset + 3] &= value; + } else { + p[offset] &= value; + p[offset + 1] &= value >> 8; + p[offset + 2] &= value >> 16; + p[offset + 3] &= value >> 24; + } + pflash_update(pfl, offset, 4); + break; } - pflash_update(pfl, offset, 2); - break; - case 4: - if (be) { - p[offset] &= value >> 24; - p[offset + 1] &= value >> 16; - p[offset + 2] &= value >> 8; - p[offset + 3] &= value; - } else { - p[offset] &= value; - p[offset + 1] &= value >> 8; - p[offset + 2] &= value >> 16; - p[offset + 3] &= value >> 24; - } - pflash_update(pfl, offset, 4); - break; } pfl->status = 0x00 | ~(value & 0x80); /* Let's pretend write is immediate */ @@ -404,9 +406,11 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, } /* Chip erase */ DPRINTF("%s: start chip erase\n", __func__); - memset(pfl->storage, 0xFF, pfl->chip_len); + if (!pfl->ro) { + memset(pfl->storage, 0xFF, pfl->chip_len); + pflash_update(pfl, 0, pfl->chip_len); + } pfl->status = 0x00; - pflash_update(pfl, 0, pfl->chip_len); /* Let's wait 5 seconds before chip erase is done */ qemu_mod_timer(pfl->timer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5)); @@ -417,8 +421,10 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, offset &= ~(pfl->sector_len - 1); DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__, offset); - memset(p + offset, 0xFF, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); + if (!pfl->ro) { + memset(p + offset, 0xFF, pfl->sector_len); + pflash_update(pfl, offset, pfl->sector_len); + } pfl->status = 0x00; /* Let's wait 1/2 second before sector erase is done */ qemu_mod_timer(pfl->timer, @@ -645,16 +651,17 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, } bdrv_attach_dev_nofail(pfl->bs, pfl); } + pflash_setup_mappings(pfl); pfl->rom_mode = 1; memory_region_add_subregion(get_system_memory(), pfl->base, &pfl->mem); -#if 0 /* XXX: there should be a bit to set up read-only, - * the same way the hardware does (with WP pin). - */ - pfl->ro = 1; -#else - pfl->ro = 0; -#endif + + if (pfl->bs) { + pfl->ro = bdrv_is_read_only(pfl->bs); + } else { + pfl->ro = 0; + } + pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl); pfl->sector_len = sector_len; pfl->width = width; diff --git a/hw/piix4.c b/hw/piix4.c index 4e7a2370cd..ce4eb0d1ae 100644 --- a/hw/piix4.c +++ b/hw/piix4.c @@ -124,8 +124,9 @@ static TypeInfo piix4_info = { .class_init = piix4_class_init, }; -static void piix4_register(void) +static void piix4_register_types(void) { type_register_static(&piix4_info); } -device_init(piix4_register); + +type_init(piix4_register_types) diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 190642733f..e0268fe053 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -589,11 +589,12 @@ static TypeInfo i440fx_pcihost_info = { .class_init = i440fx_pcihost_class_init, }; -static void i440fx_register(void) +static void i440fx_register_types(void) { type_register_static(&i440fx_info); type_register_static(&piix3_info); type_register_static(&piix3_xen_info); type_register_static(&i440fx_pcihost_info); } -device_init(i440fx_register); + +type_init(i440fx_register_types) diff --git a/hw/pl011.c b/hw/pl011.c index 752cbf9725..8a5a8f554a 100644 --- a/hw/pl011.c +++ b/hw/pl011.c @@ -316,10 +316,10 @@ static TypeInfo pl011_luminary_info = { .class_init = pl011_luminary_class_init, }; -static void pl011_register_devices(void) +static void pl011_register_types(void) { type_register_static(&pl011_arm_info); type_register_static(&pl011_luminary_info); } -device_init(pl011_register_devices) +type_init(pl011_register_types) diff --git a/hw/pl022.c b/hw/pl022.c index 30bd3442bb..60e35daeb5 100644 --- a/hw/pl022.c +++ b/hw/pl022.c @@ -9,7 +9,6 @@ #include "sysbus.h" #include "ssi.h" -#include "primecell.h" //#define DEBUG_PL022 1 @@ -299,9 +298,9 @@ static TypeInfo pl022_info = { .class_init = pl022_class_init, }; -static void pl022_register_devices(void) +static void pl022_register_types(void) { type_register_static(&pl022_info); } -device_init(pl022_register_devices) +type_init(pl022_register_types) diff --git a/hw/pl031.c b/hw/pl031.c index 8416a609ef..69abc4f345 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -76,7 +76,7 @@ static void pl031_interrupt(void * opaque) { pl031_state *s = (pl031_state *)opaque; - s->im = 1; + s->is = 1; DPRINTF("Alarm raised\n"); pl031_update(s); } @@ -230,9 +230,9 @@ static TypeInfo pl031_info = { .class_init = pl031_class_init, }; -static void pl031_register_devices(void) +static void pl031_register_types(void) { type_register_static(&pl031_info); } -device_init(pl031_register_devices) +type_init(pl031_register_types) diff --git a/hw/pl041.c b/hw/pl041.c index 6d99c9cbbc..b6723be0a9 100644 --- a/hw/pl041.c +++ b/hw/pl041.c @@ -638,9 +638,9 @@ static TypeInfo pl041_device_info = { .class_init = pl041_device_class_init, }; -static void pl041_register_device(void) +static void pl041_register_types(void) { type_register_static(&pl041_device_info); } -device_init(pl041_register_device) +type_init(pl041_register_types) diff --git a/hw/pl050.c b/hw/pl050.c index b0094ac9b3..b13924a160 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -189,10 +189,10 @@ static TypeInfo pl050_mouse_info = { .class_init = pl050_mouse_class_init, }; -static void pl050_register_devices(void) +static void pl050_register_types(void) { type_register_static(&pl050_kbd_info); type_register_static(&pl050_mouse_info); } -device_init(pl050_register_devices) +type_init(pl050_register_types) diff --git a/hw/pl061.c b/hw/pl061.c index 3136c99423..2aac7e8e9e 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -325,10 +325,10 @@ static TypeInfo pl061_luminary_info = { .class_init = pl061_luminary_class_init, }; -static void pl061_register_devices(void) +static void pl061_register_types(void) { type_register_static(&pl061_info); type_register_static(&pl061_luminary_info); } -device_init(pl061_register_devices) +type_init(pl061_register_types) diff --git a/hw/pl080.c b/hw/pl080.c index 4405d1823b..b3cf651ab7 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -409,10 +409,10 @@ static TypeInfo pl081_info = { /* The PL080 and PL081 are the same except for the number of channels they implement (8 and 2 respectively). */ -static void pl080_register_devices(void) +static void pl080_register_types(void) { type_register_static(&pl080_info); type_register_static(&pl081_info); } -device_init(pl080_register_devices) +type_init(pl080_register_types) diff --git a/hw/pl110.c b/hw/pl110.c index 86e95a3ffd..f94608cb62 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -520,11 +520,11 @@ static TypeInfo pl111_info = { .class_init = pl111_class_init, }; -static void pl110_register_devices(void) +static void pl110_register_types(void) { type_register_static(&pl110_info); type_register_static(&pl110_versatile_info); type_register_static(&pl111_info); } -device_init(pl110_register_devices) +type_init(pl110_register_types) diff --git a/hw/pl181.c b/hw/pl181.c index ae636e220e..7d91fbba1d 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -505,9 +505,9 @@ static TypeInfo pl181_info = { .class_init = pl181_class_init, }; -static void pl181_register_devices(void) +static void pl181_register_types(void) { type_register_static(&pl181_info); } -device_init(pl181_register_devices) +type_init(pl181_register_types) diff --git a/hw/pl190.c b/hw/pl190.c index 956ab21c6a..cb50afb9f4 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -273,9 +273,9 @@ static TypeInfo pl190_info = { .class_init = pl190_class_init, }; -static void pl190_register_devices(void) +static void pl190_register_types(void) { type_register_static(&pl190_info); } -device_init(pl190_register_devices) +type_init(pl190_register_types) diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index d11f120d32..203c3cdc47 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -400,9 +400,10 @@ static TypeInfo ppc4xx_pcihost_info = { .class_init = ppc4xx_pcihost_class_init, }; -static void ppc4xx_pci_register(void) +static void ppc4xx_pci_register_types(void) { type_register_static(&ppc4xx_pcihost_info); type_register_static(&ppc4xx_host_bridge_info); } -device_init(ppc4xx_pci_register); + +type_init(ppc4xx_pci_register_types) diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index d5bce71429..0f60b24134 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -373,9 +373,10 @@ static TypeInfo e500_pcihost_info = { .class_init = e500_pcihost_class_init, }; -static void e500_pci_register(void) +static void e500_pci_register_types(void) { type_register_static(&e500_pcihost_info); type_register_static(&e500_host_bridge_info); } -device_init(e500_pci_register); + +type_init(e500_pci_register_types) diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c index 9d648ec90a..6b8a189c0e 100644 --- a/hw/ppce500_spin.c +++ b/hw/ppce500_spin.c @@ -217,8 +217,9 @@ static TypeInfo ppce500_spin_info = { .class_init = ppce500_spin_class_init, }; -static void ppce500_spin_register(void) +static void ppce500_spin_register_types(void) { type_register_static(&ppce500_spin_info); } -device_init(ppce500_spin_register); + +type_init(ppce500_spin_register_types) diff --git a/hw/prep_pci.c b/hw/prep_pci.c index 40b8bb0edd..8b29da9948 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -173,10 +173,10 @@ static TypeInfo raven_pcihost_info = { .class_init = raven_pcihost_class_init, }; -static void raven_register_devices(void) +static void raven_register_types(void) { type_register_static(&raven_pcihost_info); type_register_static(&raven_info); } -device_init(raven_register_devices) +type_init(raven_register_types) diff --git a/hw/primecell.h b/hw/primecell.h index de7d6f2df2..7337c3b3ca 100644 --- a/hw/primecell.h +++ b/hw/primecell.h @@ -5,12 +5,6 @@ /* Also includes some devices that are currently only used by the ARM boards. */ -/* pl080.c */ -void *pl080_init(uint32_t base, qemu_irq irq, int nchannels); - -/* arm_sysctl.c */ -void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id); - /* arm_sysctl GPIO lines */ #define ARM_SYSCTL_GPIO_MMC_WPROT 0 #define ARM_SYSCTL_GPIO_MMC_CARDIN 1 @@ -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.c b/hw/pxa2xx.c index 244c614510..1ab27012c1 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -2330,7 +2330,7 @@ static TypeInfo pxa2xx_ssp_info = { .class_init = pxa2xx_ssp_class_init, }; -static void pxa2xx_register_devices(void) +static void pxa2xx_register_types(void) { type_register_static(&pxa2xx_i2c_slave_info); type_register_static(&pxa2xx_ssp_info); @@ -2338,4 +2338,4 @@ static void pxa2xx_register_devices(void) type_register_static(&pxa2xx_rtc_sysbus_info); } -device_init(pxa2xx_register_devices) +type_init(pxa2xx_register_types) diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index 2d61565f1d..8ced0dd8ec 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -566,8 +566,9 @@ static TypeInfo pxa2xx_dma_info = { .class_init = pxa2xx_dma_class_init, }; -static void pxa2xx_dma_register(void) +static void pxa2xx_dma_register_types(void) { type_register_static(&pxa2xx_dma_info); } -device_init(pxa2xx_dma_register); + +type_init(pxa2xx_dma_register_types) diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c index 67fd17c4ca..d5f57162ed 100644 --- a/hw/pxa2xx_gpio.c +++ b/hw/pxa2xx_gpio.c @@ -340,8 +340,9 @@ static TypeInfo pxa2xx_gpio_info = { .class_init = pxa2xx_gpio_class_init, }; -static void pxa2xx_gpio_register(void) +static void pxa2xx_gpio_register_types(void) { type_register_static(&pxa2xx_gpio_info); } -device_init(pxa2xx_gpio_register); + +type_init(pxa2xx_gpio_register_types) diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index 4e9f7b48ac..fcbdfb3fba 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -308,9 +308,12 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s) } else descptr = s->dma_ch[i].descriptor; - if (!(descptr >= PXA2XX_SDRAM_BASE && descptr + - sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size)) + if (!((descptr >= PXA2XX_SDRAM_BASE && descptr + + sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size) || + (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <= + PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { continue; + } cpu_physical_memory_read(descptr, (void *)&desc, sizeof(desc)); s->dma_ch[i].descriptor = tswap32(desc.fdaddr); @@ -830,8 +833,10 @@ static void pxa2xx_update_display(void *opaque) continue; } fbptr = s->dma_ch[ch].source; - if (!(fbptr >= PXA2XX_SDRAM_BASE && - fbptr <= PXA2XX_SDRAM_BASE + ram_size)) { + if (!((fbptr >= PXA2XX_SDRAM_BASE && + fbptr <= PXA2XX_SDRAM_BASE + ram_size) || + (fbptr >= PXA2XX_INTERNAL_BASE && + fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { pxa2xx_dma_ber_set(s, ch); continue; } @@ -894,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; @@ -1004,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/pxa2xx_pic.c b/hw/pxa2xx_pic.c index ca85743aea..6b2bdb0df1 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -313,8 +313,9 @@ static TypeInfo pxa2xx_pic_info = { .class_init = pxa2xx_pic_class_init, }; -static void pxa2xx_pic_register(void) +static void pxa2xx_pic_register_types(void) { type_register_static(&pxa2xx_pic_info); } -device_init(pxa2xx_pic_register); + +type_init(pxa2xx_pic_register_types) diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index 90800751b1..77b033b541 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -527,9 +527,10 @@ static TypeInfo pxa27x_timer_dev_info = { .class_init = pxa27x_timer_dev_class_init, }; -static void pxa2xx_timer_register(void) +static void pxa2xx_timer_register_types(void) { type_register_static(&pxa25x_timer_dev_info); type_register_static(&pxa27x_timer_dev_info); -}; -device_init(pxa2xx_timer_register); +} + +type_init(pxa2xx_timer_register_types) diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index 49f13ca087..a310cc7b16 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -457,6 +457,16 @@ DeviceState *qdev_device_add(QemuOpts *opts) id = qemu_opts_id(opts); if (id) { qdev->id = id; + } + if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) { + qdev_free(qdev); + return NULL; + } + if (qdev_init(qdev) < 0) { + qerror_report(QERR_DEVICE_INIT_FAILED, driver); + return NULL; + } + if (qdev->id) { object_property_add_child(qdev_get_peripheral(), qdev->id, OBJECT(qdev), NULL); } else { @@ -466,14 +476,6 @@ DeviceState *qdev_device_add(QemuOpts *opts) OBJECT(qdev), NULL); g_free(name); } - if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) { - qdev_free(qdev); - return NULL; - } - if (qdev_init(qdev) < 0) { - qerror_report(QERR_DEVICE_INIT_FAILED, driver); - return NULL; - } qdev->opts = opts; return qdev; } diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index b6d6fcff01..0423af1c31 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -27,16 +27,6 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val) } /* Bit */ -static int parse_bit(DeviceState *dev, Property *prop, const char *str) -{ - if (!strcasecmp(str, "on")) - bit_prop_set(dev, prop, true); - else if (!strcasecmp(str, "off")) - bit_prop_set(dev, prop, false); - else - return -EINVAL; - return 0; -} static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) { @@ -79,7 +69,6 @@ static void set_bit(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_bit = { .name = "boolean", .legacy_name = "on/off", - .parse = parse_bit, .print = print_bit, .get = get_bit, .set = set_bit, @@ -87,26 +76,6 @@ PropertyInfo qdev_prop_bit = { /* --- 8bit integer --- */ -static int parse_uint8(DeviceState *dev, Property *prop, const char *str) -{ - uint8_t *ptr = qdev_get_prop_ptr(dev, prop); - char *end; - - /* accept both hex and decimal */ - *ptr = strtoul(str, &end, 0); - if ((*end != '\0') || (end == str)) { - return -EINVAL; - } - - return 0; -} - -static int print_uint8(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - uint8_t *ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%" PRIu8, *ptr); -} - static void get_int8(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -149,8 +118,6 @@ static void set_int8(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_uint8 = { .name = "uint8", - .parse = parse_uint8, - .print = print_uint8, .get = get_int8, .set = set_int8, .min = 0, @@ -164,6 +131,10 @@ static int parse_hex8(DeviceState *dev, Property *prop, const char *str) uint8_t *ptr = qdev_get_prop_ptr(dev, prop); char *end; + if (str[0] != '0' || str[1] != 'x') { + return -EINVAL; + } + *ptr = strtoul(str, &end, 16); if ((*end != '\0') || (end == str)) { return -EINVAL; @@ -191,26 +162,6 @@ PropertyInfo qdev_prop_hex8 = { /* --- 16bit integer --- */ -static int parse_uint16(DeviceState *dev, Property *prop, const char *str) -{ - uint16_t *ptr = qdev_get_prop_ptr(dev, prop); - char *end; - - /* accept both hex and decimal */ - *ptr = strtoul(str, &end, 0); - if ((*end != '\0') || (end == str)) { - return -EINVAL; - } - - return 0; -} - -static int print_uint16(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - uint16_t *ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%" PRIu16, *ptr); -} - static void get_int16(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -253,8 +204,6 @@ static void set_int16(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_uint16 = { .name = "uint16", - .parse = parse_uint16, - .print = print_uint16, .get = get_int16, .set = set_int16, .min = 0, @@ -263,26 +212,6 @@ PropertyInfo qdev_prop_uint16 = { /* --- 32bit integer --- */ -static int parse_uint32(DeviceState *dev, Property *prop, const char *str) -{ - uint32_t *ptr = qdev_get_prop_ptr(dev, prop); - char *end; - - /* accept both hex and decimal */ - *ptr = strtoul(str, &end, 0); - if ((*end != '\0') || (end == str)) { - return -EINVAL; - } - - return 0; -} - -static int print_uint32(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - uint32_t *ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%" PRIu32, *ptr); -} - static void get_int32(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -325,37 +254,14 @@ static void set_int32(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_uint32 = { .name = "uint32", - .parse = parse_uint32, - .print = print_uint32, .get = get_int32, .set = set_int32, .min = 0, .max = 0xFFFFFFFFULL, }; -static int parse_int32(DeviceState *dev, Property *prop, const char *str) -{ - int32_t *ptr = qdev_get_prop_ptr(dev, prop); - char *end; - - *ptr = strtol(str, &end, 10); - if ((*end != '\0') || (end == str)) { - return -EINVAL; - } - - return 0; -} - -static int print_int32(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - int32_t *ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%" PRId32, *ptr); -} - PropertyInfo qdev_prop_int32 = { .name = "int32", - .parse = parse_int32, - .print = print_int32, .get = get_int32, .set = set_int32, .min = -0x80000000LL, @@ -369,6 +275,10 @@ static int parse_hex32(DeviceState *dev, Property *prop, const char *str) uint32_t *ptr = qdev_get_prop_ptr(dev, prop); char *end; + if (str[0] != '0' || str[1] != 'x') { + return -EINVAL; + } + *ptr = strtoul(str, &end, 16); if ((*end != '\0') || (end == str)) { return -EINVAL; @@ -396,26 +306,6 @@ PropertyInfo qdev_prop_hex32 = { /* --- 64bit integer --- */ -static int parse_uint64(DeviceState *dev, Property *prop, const char *str) -{ - uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - char *end; - - /* accept both hex and decimal */ - *ptr = strtoull(str, &end, 0); - if ((*end != '\0') || (end == str)) { - return -EINVAL; - } - - return 0; -} - -static int print_uint64(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%" PRIu64, *ptr); -} - static void get_int64(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -443,8 +333,6 @@ static void set_int64(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_uint64 = { .name = "uint64", - .parse = parse_uint64, - .print = print_uint64, .get = get_int64, .set = set_int64, }; @@ -456,6 +344,10 @@ static int parse_hex64(DeviceState *dev, Property *prop, const char *str) uint64_t *ptr = qdev_get_prop_ptr(dev, prop); char *end; + if (str[0] != '0' || str[1] != 'x') { + return -EINVAL; + } + *ptr = strtoull(str, &end, 16); if ((*end != '\0') || (end == str)) { return -EINVAL; @@ -613,7 +505,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop, } if (!*str) { g_free(str); - error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); + *ptr = NULL; return; } ret = parse(dev, str, ptr); @@ -737,19 +629,6 @@ PropertyInfo qdev_prop_netdev = { /* --- vlan --- */ -static int parse_vlan(DeviceState *dev, Property *prop, const char *str) -{ - VLANState **ptr = qdev_get_prop_ptr(dev, prop); - int id; - - if (sscanf(str, "%d", &id) != 1) - return -EINVAL; - *ptr = qemu_find_vlan(id, 1); - if (*ptr == NULL) - return -ENOENT; - return 0; -} - static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) { VLANState **ptr = qdev_get_prop_ptr(dev, prop); @@ -808,7 +687,6 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, PropertyInfo qdev_prop_vlan = { .name = "vlan", - .parse = parse_vlan, .print = print_vlan, .get = get_vlan, .set = set_vlan, @@ -943,25 +821,40 @@ PropertyInfo qdev_prop_losttickpolicy = { /* * bus-local address, i.e. "$slot" or "$slot.$fn" */ -static int parse_pci_devfn(DeviceState *dev, Property *prop, const char *str) +static void set_pci_devfn(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) { + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; uint32_t *ptr = qdev_get_prop_ptr(dev, prop); unsigned int slot, fn, n; + Error *local_err = NULL; + char *str = (char *)""; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_str(v, &str, name, &local_err); + if (local_err) { + return set_int32(obj, v, opaque, name, errp); + } if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { fn = 0; if (sscanf(str, "%x%n", &slot, &n) != 1) { - return -EINVAL; + goto invalid; } } - if (str[n] != '\0') - return -EINVAL; - if (fn > 7) - return -EINVAL; - if (slot > 31) - return -EINVAL; + if (str[n] != '\0' || fn > 7 || slot > 31) { + goto invalid; + } *ptr = slot << 3 | fn; - return 0; + return; + +invalid: + error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); } static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t len) @@ -978,10 +871,9 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t PropertyInfo qdev_prop_pci_devfn = { .name = "int32", .legacy_name = "pci-devfn", - .parse = parse_pci_devfn, .print = print_pci_devfn, .get = get_int32, - .set = set_int32, + .set = set_pci_devfn, /* FIXME: this should be -1...255, but the address is stored * into an uint32_t rather than int32_t. */ @@ -1054,9 +946,9 @@ int qdev_prop_parse(DeviceState *dev, const char *name, const char *value) legacy_name = g_strdup_printf("legacy-%s", name); if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) { - object_property_set_str(OBJECT(dev), value, legacy_name, &err); + object_property_parse(OBJECT(dev), value, legacy_name, &err); } else { - object_property_set_str(OBJECT(dev), value, name, &err); + object_property_parse(OBJECT(dev), value, name, &err); } g_free(legacy_name); @@ -1072,55 +964,56 @@ void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) { Error *errp = NULL; object_property_set_bool(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_string(DeviceState *dev, const char *name, char *value) { Error *errp = NULL; object_property_set_str(OBJECT(dev), value, name, &errp); - assert(!errp); + assert_no_error(errp); } int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) { Error *errp = NULL; - object_property_set_str(OBJECT(dev), bdrv_get_device_name(value), + const char *bdrv_name = value ? bdrv_get_device_name(value) : ""; + object_property_set_str(OBJECT(dev), bdrv_name, name, &errp); if (errp) { qerror_report_err(errp); @@ -1139,24 +1032,26 @@ void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverS void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { Error *errp = NULL; - assert(value->label); - object_property_set_str(OBJECT(dev), value->label, name, &errp); - assert(!errp); + assert(!value || value->label); + object_property_set_str(OBJECT(dev), + value ? value->label : "", name, &errp); + assert_no_error(errp); } void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value) { Error *errp = NULL; - assert(value->name); - object_property_set_str(OBJECT(dev), value->name, name, &errp); - assert(!errp); + assert(!value || value->name); + object_property_set_str(OBJECT(dev), + value ? value->name : "", name, &errp); + assert_no_error(errp); } void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value) { Error *errp = NULL; object_property_set_int(OBJECT(dev), value ? value->id : -1, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) @@ -1167,7 +1062,7 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) value[0], value[1], value[2], value[3], value[4], value[5]); object_property_set_str(OBJECT(dev), str, name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) @@ -1178,7 +1073,7 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) prop = qdev_prop_find(dev, name); object_property_set_str(OBJECT(dev), prop->info->enum_table[value], name, &errp); - assert(!errp); + assert_no_error(errp); } void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) @@ -1210,7 +1105,7 @@ void qdev_prop_set_defaults(DeviceState *dev, Property *props) } else if (props->qtype == QTYPE_QINT) { object_property_set_int(obj, props->defval, props->name, &errp); } - assert(!errp); + assert_no_error(errp); } } @@ -117,6 +117,9 @@ DeviceState *qdev_try_create(BusState *bus, const char *name) { DeviceState *dev; + if (object_class_by_name(name) == NULL) { + return NULL; + } dev = DEVICE(object_new(name)); if (!dev) { return NULL; @@ -665,9 +668,9 @@ static TypeInfo device_type_info = { .class_size = sizeof(DeviceClass), }; -static void init_qdev(void) +static void qdev_register_types(void) { type_register_static(&device_type_info); } -device_init(init_qdev); +type_init(qdev_register_types) diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 133d09324c..25857f6a20 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -21,14 +21,31 @@ #include "qxl.h" -static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) +static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { - uint8_t *src = qxl->guest_primary.data; - uint8_t *dst = qxl->guest_primary.flipped; + uint8_t *src; + uint8_t *dst = qxl->vga.ds->surface->data; int len, i; - src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.abs_stride; + if (is_buffer_shared(qxl->vga.ds->surface)) { + return; + } + if (!qxl->guest_primary.data) { + dprint(qxl, 1, "%s: initializing guest_primary.data\n", __func__); + qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + } + dprint(qxl, 2, "%s: stride %d, [%d, %d, %d, %d]\n", __func__, + qxl->guest_primary.qxl_stride, + rect->left, rect->right, rect->top, rect->bottom); + src = qxl->guest_primary.data; + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl surface is upside down, walk src scanlines + * in reverse order to flip it */ + src += (qxl->guest_primary.surface.height - rect->top - 1) * + qxl->guest_primary.abs_stride; + } else { + src += rect->top * qxl->guest_primary.abs_stride; + } dst += rect->top * qxl->guest_primary.abs_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; @@ -37,7 +54,7 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); dst += qxl->guest_primary.abs_stride; - src -= qxl->guest_primary.abs_stride; + src += qxl->guest_primary.qxl_stride; } } @@ -71,86 +88,109 @@ void qxl_render_resize(PCIQXLDevice *qxl) } } -void qxl_render_update(PCIQXLDevice *qxl) +static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) +{ + area->left = 0; + area->right = qxl->guest_primary.surface.width; + area->top = 0; + area->bottom = qxl->guest_primary.surface.height; +} + +static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; - QXLRect dirty[32], update; - void *ptr; - int i, redraw = 0; - - if (!is_buffer_shared(vga->ds->surface)) { - dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__); - qxl->guest_primary.resized++; - qxl->guest_primary.commands++; - redraw = 1; - } + int i; + DisplaySurface *surface = vga->ds->surface; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; - - if (qxl->guest_primary.flipped) { - g_free(qxl->guest_primary.flipped); - qxl->guest_primary.flipped = NULL; - } - qemu_free_displaysurface(vga->ds); - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - if (qxl->guest_primary.qxl_stride < 0) { - /* spice surface is upside down -> need extra buffer to flip */ - qxl->guest_primary.flipped = - g_malloc(qxl->guest_primary.surface.width * - qxl->guest_primary.abs_stride); - ptr = qxl->guest_primary.flipped; - } else { - ptr = qxl->guest_primary.data; - } - dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", + qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); + qxl->num_dirty_rects = 1; + dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d\n", __FUNCTION__, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, - qxl->guest_primary.bits_pp, - qxl->guest_primary.flipped ? "yes" : "no"); - vga->ds->surface = + qxl->guest_primary.bits_pp); + } + if (surface->width != qxl->guest_primary.surface.width || + surface->height != qxl->guest_primary.surface.height) { + if (qxl->guest_primary.qxl_stride > 0) { + dprint(qxl, 1, "%s: using guest_primary for displaysurface\n", + __func__); + qemu_free_displaysurface(vga->ds); qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, - ptr); - dpy_resize(vga->ds); + qxl->guest_primary.data); + } else { + dprint(qxl, 1, "%s: resizing displaysurface to guest_primary\n", + __func__); + qemu_resize_displaysurface(vga->ds, + qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height); + } + } + for (i = 0; i < qxl->num_dirty_rects; i++) { + if (qemu_spice_rect_is_empty(qxl->dirty+i)) { + break; + } + qxl_blit(qxl, qxl->dirty+i); + dpy_update(vga->ds, + qxl->dirty[i].left, qxl->dirty[i].top, + qxl->dirty[i].right - qxl->dirty[i].left, + qxl->dirty[i].bottom - qxl->dirty[i].top); } + qxl->num_dirty_rects = 0; +} - if (!qxl->guest_primary.commands) { +/* + * use ssd.lock to protect render_update_cookie_num. + * qxl_render_update is called by io thread or vcpu thread, and the completion + * callbacks are called by spice_server thread, defering to bh called from the + * io thread. + */ +void qxl_render_update(PCIQXLDevice *qxl) +{ + QXLCookie *cookie; + + qemu_mutex_lock(&qxl->ssd.lock); + + if (!runstate_is_running() || !qxl->guest_primary.commands) { + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); return; } + qxl->guest_primary.commands = 0; + qxl->render_update_cookie_num++; + qemu_mutex_unlock(&qxl->ssd.lock); + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, + 0); + qxl_set_rect_to_surface(qxl, &cookie->u.render.area); + qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, + 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); +} - update.left = 0; - update.right = qxl->guest_primary.surface.width; - update.top = 0; - update.bottom = qxl->guest_primary.surface.height; - - memset(dirty, 0, sizeof(dirty)); - qxl_spice_update_area(qxl, 0, &update, - dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); - if (redraw) { - memset(dirty, 0, sizeof(dirty)); - dirty[0] = update; - } +void qxl_render_update_area_bh(void *opaque) +{ + PCIQXLDevice *qxl = opaque; - for (i = 0; i < ARRAY_SIZE(dirty); i++) { - if (qemu_spice_rect_is_empty(dirty+i)) { - break; - } - if (qxl->guest_primary.flipped) { - qxl_flip(qxl, dirty+i); - } - dpy_update(vga->ds, - dirty[i].left, dirty[i].top, - dirty[i].right - dirty[i].left, - dirty[i].bottom - dirty[i].top); - } + qemu_mutex_lock(&qxl->ssd.lock); + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) +{ + qemu_mutex_lock(&qxl->ssd.lock); + qemu_bh_schedule(qxl->update_area_bh); + qxl->render_update_cookie_num--; + qemu_mutex_unlock(&qxl->ssd.lock); + g_free(cookie); } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) @@ -125,9 +125,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl); void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 qxl_send_events(qxl, QXL_INTERRUPT_ERROR); -#endif if (qxl->guestdebug) { va_list ap; va_start(ap, msg); @@ -143,18 +141,15 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async) + qxl_async_io async, struct QXLCookie *cookie) { if (async == QXL_SYNC) { qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, dirty_rects, num_dirty_rects, clear_dirty_region); } else { -#if SPICE_INTERFACE_QXL_MINOR >= 1 + assert(cookie != NULL); spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, - clear_dirty_region, 0); -#else - abort(); -#endif + clear_dirty_region, (uint64_t)cookie); } } @@ -170,25 +165,25 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, qxl_async_io async) { + QXLCookie *cookie; + if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, - (uint64_t)id); -#endif + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_SURFACE_ASYNC); + cookie->u.surface_id = id; + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uint64_t)cookie); } else { qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); qxl_spice_destroy_surface_wait_complete(qxl, id); } } -#if SPICE_INTERFACE_QXL_MINOR >= 1 static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) { - spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0); + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_FLUSH_SURFACES_ASYNC)); } -#endif void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count) @@ -217,11 +212,9 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) { if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0); -#endif + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); } else { qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); qxl_spice_destroy_surfaces_complete(qxl); @@ -490,7 +483,6 @@ static const char *io_port_to_string(uint32_t io_port) [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", -#if SPICE_INTERFACE_QXL_MINOR >= 1 [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", @@ -500,7 +492,6 @@ static const char *io_port_to_string(uint32_t io_port) = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", -#endif }; return io_port_to_string[io_port]; } @@ -625,7 +616,7 @@ static void interface_release_resource(QXLInstance *sin, if (ext.group_id == MEMSLOT_GROUP_HOST) { /* host group -> vga mode update request */ - qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id); + qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id); return; } @@ -735,12 +726,9 @@ static int interface_flush_resources(QXLInstance *sin) static void qxl_create_guest_primary_complete(PCIQXLDevice *d); -#if SPICE_INTERFACE_QXL_MINOR >= 1 - /* called from spice server thread context only */ -static void interface_async_complete(QXLInstance *sin, uint64_t cookie) +static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) { - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); uint32_t current_async; qemu_mutex_lock(&qxl->async_lock); @@ -748,8 +736,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&qxl->async_lock); - dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie); + dprint(qxl, 2, "async_complete: %d (%p) done\n", current_async, cookie); + if (!cookie) { + fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); + return; + } + if (cookie && current_async != cookie->io) { + fprintf(stderr, + "qxl: %s: error: current_async = %d != %ld = cookie->io\n", + __func__, current_async, cookie->io); + } switch (current_async) { + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_DESTROY_PRIMARY_ASYNC: + case QXL_IO_UPDATE_AREA_ASYNC: + case QXL_IO_FLUSH_SURFACES_ASYNC: + break; case QXL_IO_CREATE_PRIMARY_ASYNC: qxl_create_guest_primary_complete(qxl); break; @@ -757,13 +759,75 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl_spice_destroy_surfaces_complete(qxl); break; case QXL_IO_DESTROY_SURFACE_ASYNC: - qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie); + qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); break; + default: + fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, + current_async); } qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); } -#endif +/* called from spice server thread context only */ +static void interface_update_area_complete(QXLInstance *sin, + uint32_t surface_id, + QXLRect *dirty, uint32_t num_updated_rects) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int i; + int qxl_i; + + qemu_mutex_lock(&qxl->ssd.lock); + if (surface_id != 0 || !qxl->render_update_cookie_num) { + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { + /* + * overflow - treat this as a full update. Not expected to be common. + */ + dprint(qxl, 1, "%s: overflow of dirty rects\n", __func__); + qxl->guest_primary.resized = 1; + } + if (qxl->guest_primary.resized) { + /* + * Don't bother copying or scheduling the bh since we will flip + * the whole area anyway on completion of the update_area async call + */ + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + qxl_i = qxl->num_dirty_rects; + for (i = 0; i < num_updated_rects; i++) { + qxl->dirty[qxl_i++] = dirty[i]; + } + qxl->num_dirty_rects += num_updated_rects; + dprint(qxl, 1, "%s: scheduling update_area_bh, #dirty %d\n", + __func__, qxl->num_dirty_rects); + qemu_bh_schedule(qxl->update_area_bh); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCookie *cookie = (QXLCookie *)cookie_token; + + switch (cookie->type) { + case QXL_COOKIE_TYPE_IO: + interface_async_complete_io(qxl, cookie); + g_free(cookie); + break; + case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: + qxl_render_update_area_done(qxl, cookie); + break; + default: + fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", + __func__, cookie->type); + g_free(cookie); + } +} static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, @@ -784,9 +848,8 @@ static const QXLInterface qxl_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, -#if SPICE_INTERFACE_QXL_MINOR >= 1 .async_complete = interface_async_complete, -#endif + .update_area_complete = interface_update_area_complete, }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -913,6 +976,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, static const int regions[] = { QXL_RAM_RANGE_INDEX, QXL_VRAM_RANGE_INDEX, + QXL_VRAM64_RANGE_INDEX, }; uint64_t guest_start; uint64_t guest_end; @@ -959,6 +1023,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); break; case QXL_VRAM_RANGE_INDEX: + case 4 /* vram 64bit */: virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); break; default: @@ -1006,7 +1071,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) qxl_spice_destroy_surfaces(d, QXL_SYNC); } -/* called from spice server thread context only */ +/* can be also called from spice server thread context */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) { uint64_t phys = le64_to_cpu(pqxl); @@ -1015,7 +1080,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) switch (group_id) { case MEMSLOT_GROUP_HOST: - return (void*)offset; + return (void *)(intptr_t)offset; case MEMSLOT_GROUP_GUEST: PANIC_ON(slot >= NUM_MEMSLOTS); PANIC_ON(!qxl->guest_slots[slot].active); @@ -1077,9 +1142,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) if (d->mode == QXL_MODE_UNDEFINED) { return 0; } - dprint(d, 1, "%s\n", __FUNCTION__); - d->mode = QXL_MODE_UNDEFINED; qemu_spice_destroy_primary_surface(&d->ssd, 0, async); qxl_spice_reset_cursor(d); @@ -1136,9 +1199,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, PCIQXLDevice *d = opaque; uint32_t io_port = addr; qxl_async_io async = QXL_SYNC; -#if SPICE_INTERFACE_QXL_MINOR >= 1 uint32_t orig_io_port = io_port; -#endif switch (io_port) { case QXL_IO_RESET: @@ -1148,10 +1209,8 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: case QXL_IO_LOG: -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_MEMSLOT_ADD_ASYNC: case QXL_IO_CREATE_PRIMARY_ASYNC: -#endif break; default: if (d->mode != QXL_MODE_VGA) { @@ -1159,17 +1218,14 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, } dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", __func__, io_port, io_port_to_string(io_port)); -#if SPICE_INTERFACE_QXL_MINOR >= 1 /* be nice to buggy guest drivers */ if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); } -#endif return; } -#if SPICE_INTERFACE_QXL_MINOR >= 1 /* we change the io_port to avoid ifdeffery in the main switch */ orig_io_port = io_port; switch (io_port) { @@ -1208,14 +1264,21 @@ async_common: default: break; } -#endif switch (io_port) { case QXL_IO_UPDATE_AREA: { + QXLCookie *cookie = NULL; QXLRect update = d->ram->update_area; + + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); + cookie->u.area = update; + } qxl_spice_update_area(d, d->ram->update_surface, - &update, NULL, 0, 0, async); + cookie ? &cookie->u.area : &update, + NULL, 0, 0, async, cookie); break; } case QXL_IO_NOTIFY_CMD: @@ -1300,7 +1363,6 @@ async_common: } qxl_spice_destroy_surface_wait(d, val, async); break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_FLUSH_RELEASE: { QXLReleaseRing *ring = &d->ram->release_ring; if (ring->prod - ring->cons + 1 == ring->num_items) { @@ -1321,7 +1383,6 @@ async_common: d->num_free_res); qxl_spice_flush_surfaces_async(d); break; -#endif case QXL_IO_DESTROY_ALL_SURFACES: d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, async); @@ -1332,16 +1393,12 @@ async_common: } return; cancel_async: -#if SPICE_INTERFACE_QXL_MINOR >= 1 if (async) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); qemu_mutex_lock(&d->async_lock); d->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&d->async_lock); } -#else - return; -#endif } static uint64_t ioport_read(void *opaque, target_phys_addr_t addr, @@ -1435,7 +1492,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; @@ -1447,7 +1504,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; @@ -1465,6 +1522,46 @@ static void qxl_hw_text_update(void *opaque, console_ch_t *chardata) } } +static void qxl_dirty_surfaces(PCIQXLDevice *qxl) +{ + intptr_t vram_start; + int i; + + if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { + return; + } + + /* dirty the primary surface */ + qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, + qxl->shadow_rom.surface0_area_size); + + vram_start = (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); + + /* dirty the off-screen surfaces */ + for (i = 0; i < NUM_SURFACES; i++) { + QXLSurfaceCmd *cmd; + intptr_t surface_offset; + int surface_size; + + if (qxl->guest_surfaces.cmds[i] == 0) { + continue; + } + + cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i], + MEMSLOT_GROUP_GUEST); + assert(cmd->type == QXL_SURFACE_CMD_CREATE); + surface_offset = (intptr_t)qxl_phys2virt(qxl, + cmd->u.surface_create.data, + MEMSLOT_GROUP_GUEST); + surface_offset -= vram_start; + surface_size = cmd->u.surface_create.height * + abs(cmd->u.surface_create.stride); + dprint(qxl, 3, "%s: dirty surface %d, offset %d, size %d\n", __func__, + i, (int)surface_offset, surface_size); + qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); + } +} + static void qxl_vm_change_state_handler(void *opaque, int running, RunState state) { @@ -1478,14 +1575,9 @@ static void qxl_vm_change_state_handler(void *opaque, int running, * called */ qxl_update_irq(qxl); - } else if (qxl->mode == QXL_MODE_NATIVE) { - /* dirty all vram (which holds surfaces) and devram (primary surface) - * to make sure they are saved */ - /* FIXME #1: should go out during "live" stage */ - /* FIXME #2: we only need to save the areas which are actually used */ - qxl_set_dirty(&qxl->vram_bar, 0, qxl->vram_size); - qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, - qxl->shadow_rom.surface0_area_size); + } else { + /* make sure surfaces are saved before migration */ + qxl_dirty_surfaces(qxl); } } @@ -1509,6 +1601,10 @@ static void display_refresh(struct DisplayState *ds) { if (qxl0->mode == QXL_MODE_VGA) { qemu_spice_display_refresh(&qxl0->ssd); + } else { + qemu_mutex_lock(&qxl0->ssd.lock); + qemu_spice_cursor_refresh_unlocked(&qxl0->ssd); + qemu_mutex_unlock(&qxl0->ssd.lock); } } @@ -1518,6 +1614,41 @@ static DisplayChangeListener display_listener = { .dpy_refresh = display_refresh, }; +static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb) +{ + /* vga ram (bar 0) */ + if (qxl->ram_size_mb != -1) { + qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; + } + if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) { + qxl->vga.vram_size = ram_min_mb * 1024 * 1024; + } + + /* vram32 (surfaces, 32bit, bar 1) */ + if (qxl->vram32_size_mb != -1) { + qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + } + if (qxl->vram32_size < 4096) { + qxl->vram32_size = 4096; + } + + /* vram (surfaces, 64bit, bar 4+5) */ + if (qxl->vram_size_mb != -1) { + qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; + } + if (qxl->vram_size < qxl->vram32_size) { + qxl->vram_size = qxl->vram32_size; + } + + if (qxl->revision == 1) { + qxl->vram32_size = 4096; + qxl->vram_size = 4096; + } + qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); + qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); +} + static int qxl_init_common(PCIQXLDevice *qxl) { uint8_t* config = qxl->pci.config; @@ -1539,9 +1670,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) case 2: /* spice 0.6 -- qxl-2 */ pci_device_rev = QXL_REVISION_STABLE_V06; break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case 3: /* qxl-3 */ -#endif default: pci_device_rev = QXL_DEFAULT_REVISION; break; @@ -1556,15 +1685,10 @@ static int qxl_init_common(PCIQXLDevice *qxl) init_qxl_rom(qxl); init_qxl_ram(qxl); - if (qxl->vram_size < 16 * 1024 * 1024) { - qxl->vram_size = 16 * 1024 * 1024; - } - if (qxl->revision == 1) { - qxl->vram_size = 4096; - } - qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); + memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, + 0, qxl->vram32_size); io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); if (qxl->revision == 1) { @@ -1588,7 +1712,29 @@ static int qxl_init_common(PCIQXLDevice *qxl) PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar); + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); + + if (qxl->vram32_size < qxl->vram_size) { + /* + * Make the 64bit vram bar show up only in case it is + * configured to be larger than the 32bit vram bar. + */ + pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + &qxl->vram_bar); + } + + /* print pci bar details */ + dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", + qxl->vga.vram_size / (1024*1024)); + dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + qxl->vram32_size / (1024*1024)); + dprint(qxl, 1, "vram/64: %d MB %s\n", + qxl->vram_size / (1024*1024), + qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); qxl->ssd.qxl.base.sif = &qxl_interface.base; qxl->ssd.qxl.id = qxl->id; @@ -1598,6 +1744,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) init_pipe_signaling(qxl); qxl_reset_state(qxl); + qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); + return 0; } @@ -1605,15 +1753,11 @@ static int qxl_init_primary(PCIDevice *dev) { PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); VGACommonState *vga = &qxl->vga; - ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1); PortioList *qxl_vga_port_list = g_new(PortioList, 1); qxl->id = 0; - - if (ram_size < 32 * 1024 * 1024) { - ram_size = 32 * 1024 * 1024; - } - vga_common_init(vga, ram_size); + qxl_init_ramsize(qxl, 32); + vga_common_init(vga, qxl->vga.vram_size); vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false); portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga"); portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); @@ -1632,14 +1776,9 @@ static int qxl_init_secondary(PCIDevice *dev) { static int device_id = 1; PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); - ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1); qxl->id = device_id++; - - if (ram_size < 16 * 1024 * 1024) { - ram_size = 16 * 1024 * 1024; - } - qxl->vga.vram_size = ram_size; + qxl_init_ramsize(qxl, 16); memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size); vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); @@ -1814,13 +1953,16 @@ static VMStateDescription qxl_vmstate = { static Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, 64 * 1024 * 1024), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1), + DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, 0), + DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -1870,10 +2012,10 @@ static TypeInfo qxl_secondary_info = { .class_init = qxl_secondary_class_init, }; -static void qxl_register(void) +static void qxl_register_types(void) { type_register_static(&qxl_primary_info); type_register_static(&qxl_secondary_info); } -device_init(qxl_register); +type_init(qxl_register_types) @@ -16,8 +16,14 @@ enum qxl_mode { QXL_MODE_NATIVE, }; +#ifndef QXL_VRAM64_RANGE_INDEX +#define QXL_VRAM64_RANGE_INDEX 4 +#endif + #define QXL_UNDEFINED_IO UINT32_MAX +#define QXL_NUM_DIRTY_RECTS 64 + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; @@ -52,7 +58,7 @@ typedef struct PCIQXLDevice { uint32_t abs_stride; uint32_t bits_pp; uint32_t bytes_pp; - uint8_t *data, *flipped; + uint8_t *data; } guest_primary; struct surfaces { @@ -86,9 +92,22 @@ typedef struct PCIQXLDevice { /* vram pci bar */ uint32_t vram_size; MemoryRegion vram_bar; + uint32_t vram32_size; + MemoryRegion vram32_bar; /* io bar */ MemoryRegion io_bar; + + /* user-friendly properties (in megabytes) */ + uint32_t ram_size_mb; + uint32_t vram_size_mb; + uint32_t vram32_size_mb; + + /* qxl_render_update state */ + int render_update_cookie_num; + int num_dirty_rects; + QXLRect dirty[QXL_NUM_DIRTY_RECTS]; + QEMUBH *update_area_bh; } PCIQXLDevice; #define PANIC_ON(x) if ((x)) { \ @@ -104,11 +123,7 @@ typedef struct PCIQXLDevice { } \ } while (0) -#if SPICE_INTERFACE_QXL_MINOR >= 1 #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 -#else -#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06 -#endif /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); @@ -118,7 +133,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async); + qxl_async_io async, QXLCookie *cookie); void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count); void qxl_spice_oom(PCIQXLDevice *qxl); @@ -134,9 +149,5 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); void qxl_render_resize(PCIQXLDevice *qxl); void qxl_render_update(PCIQXLDevice *qxl); void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); -#if SPICE_INTERFACE_QXL_MINOR >= 1 -void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, - uint32_t clear_dirty_region, - int is_vga); -#endif +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie); +void qxl_render_update_area_bh(void *opaque); diff --git a/hw/realview.c b/hw/realview.c index 8b0b03d2d3..ae1bbcdac3 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -96,7 +96,7 @@ static TypeInfo realview_i2c_info = { .class_init = realview_i2c_class_init, }; -static void realview_register_devices(void) +static void realview_register_types(void) { type_register_static(&realview_i2c_info); } @@ -222,21 +222,23 @@ static void realview_init(ram_addr_t ram_size, sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); if (is_mpcore) { + target_phys_addr_t periphbase; dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore"); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); busdev = sysbus_from_qdev(dev); if (is_pb) { - realview_binfo.smp_priv_base = 0x1f000000; + periphbase = 0x1f000000; } else { - realview_binfo.smp_priv_base = 0x10100000; + periphbase = 0x10100000; } - sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base); + sysbus_mmio_map(busdev, 0, periphbase); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); } - sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000, - NULL); + sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL); + /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */ + realview_binfo.gic_cpu_if_addr = periphbase + 0x100; } else { uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000; /* For now just create the nIRQ GIC, and ignore the others. */ @@ -491,4 +493,4 @@ static void realview_machine_init(void) } machine_init(realview_machine_init); -device_init(realview_register_devices) +type_init(realview_register_types) diff --git a/hw/realview_gic.c b/hw/realview_gic.c index 4121502c7b..071ef13c9e 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -60,9 +60,9 @@ static TypeInfo realview_gic_info = { .class_init = realview_gic_class_init, }; -static void realview_gic_register_devices(void) +static void realview_gic_register_types(void) { type_register_static(&realview_gic_info); } -device_init(realview_gic_register_devices) +type_init(realview_gic_register_types) diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 1668390e1f..05b8e1e3d7 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -3523,9 +3523,9 @@ static TypeInfo rtl8139_info = { .class_init = rtl8139_class_init, }; -static void rtl8139_register_devices(void) +static void rtl8139_register_types(void) { type_register_static(&rtl8139_info); } -device_init(rtl8139_register_devices) +type_init(rtl8139_register_types) diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 49140f8a9e..c450e4bb5b 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,15 +445,26 @@ static TypeInfo virtio_s390_device_info = { .abstract = true, }; -static void s390_virtio_register(void) +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) { - type_register_static(&virtio_s390_device_info); - type_register_static(&s390_virtio_serial); - type_register_static(&s390_virtio_blk); - type_register_static(&s390_virtio_net); + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); + + k->init = s390_virtio_scsi_init; + dc->props = s390_virtio_scsi_properties; } -device_init(s390_virtio_register); +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 */ @@ -468,9 +491,14 @@ static TypeInfo s390_virtio_bridge_info = { .class_init = s390_virtio_bridge_class_init, }; -static void s390_virtio_register_devices(void) +static void s390_virtio_register_types(void) { + type_register_static(&virtio_s390_device_info); + 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); } -device_init(s390_virtio_register_devices) +type_init(s390_virtio_register_types) diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index b5e59b7d4b..0e60bc0fa2 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 { @@ -1417,8 +1417,9 @@ static TypeInfo sb16_info = { .class_init = sb16_class_initfn, }; -static void sb16_register (void) +static void sb16_register_types (void) { type_register_static (&sb16_info); } -device_init (sb16_register) + +type_init (sb16_register_types) @@ -148,9 +148,9 @@ static TypeInfo sbi_info = { .class_init = sbi_class_init, }; -static void sbi_register_devices(void) +static void sbi_register_types(void) { type_register_static(&sbi_info); } -device_init(sbi_register_devices) +type_init(sbi_register_types) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 0ee50a8360..2cb5a18da2 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); @@ -1431,9 +1566,9 @@ static TypeInfo scsi_device_type_info = { .class_init = scsi_device_class_init, }; -static void scsi_register_devices(void) +static void scsi_register_types(void) { type_register_static(&scsi_device_type_info); } -device_init(scsi_register_devices); +type_init(scsi_register_types) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 399e51e494..add399e97b 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 = { @@ -1823,7 +1924,7 @@ static TypeInfo scsi_disk_info = { .class_init = scsi_disk_class_initfn, }; -static void scsi_disk_register_devices(void) +static void scsi_disk_register_types(void) { type_register_static(&scsi_hd_info); type_register_static(&scsi_cd_info); @@ -1832,4 +1933,5 @@ static void scsi_disk_register_devices(void) #endif type_register_static(&scsi_disk_info); } -device_init(scsi_disk_register_devices) + +type_init(scsi_disk_register_types) diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 4859212734..d856d23b3b 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 = { @@ -483,10 +508,11 @@ static TypeInfo scsi_generic_info = { .class_init = scsi_generic_class_initfn, }; -static void scsi_generic_register_devices(void) +static void scsi_generic_register_types(void) { type_register_static(&scsi_generic_info); } -device_init(scsi_generic_register_devices) + +type_init(scsi_generic_register_types) #endif /* __linux__ */ @@ -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 82917e24d7..c0ee55d20c 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(), }; @@ -903,9 +909,9 @@ static TypeInfo serial_isa_info = { .class_init = serial_isa_class_initfn, }; -static void serial_register_devices(void) +static void serial_register_types(void) { type_register_static(&serial_isa_info); } -device_init(serial_register_devices) +type_init(serial_register_types) @@ -55,9 +55,9 @@ static TypeInfo sga_info = { .class_init = sga_class_initfn, }; -static void sga_register(void) +static void sga_register_types(void) { type_register_static(&sga_info); } -device_init(sga_register); +type_init(sga_register_types) diff --git a/hw/sh_pci.c b/hw/sh_pci.c index 4234d935e7..0cfac46f7f 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -177,10 +177,10 @@ static TypeInfo sh_pci_device_info = { .class_init = sh_pci_device_class_init, }; -static void sh_pci_register_devices(void) +static void sh_pci_register_types(void) { type_register_static(&sh_pci_device_info); type_register_static(&sh_pci_host_info); } -device_init(sh_pci_register_devices) +type_init(sh_pci_register_types) diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c index e3701c75d2..7fdc3be086 100644 --- a/hw/slavio_intctl.c +++ b/hw/slavio_intctl.c @@ -463,9 +463,9 @@ static TypeInfo slavio_intctl_info = { .class_init = slavio_intctl_class_init, }; -static void slavio_intctl_register_devices(void) +static void slavio_intctl_register_types(void) { type_register_static(&slavio_intctl_info); } -device_init(slavio_intctl_register_devices) +type_init(slavio_intctl_register_types) diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c index 5a025186af..944835e880 100644 --- a/hw/slavio_misc.c +++ b/hw/slavio_misc.c @@ -499,10 +499,10 @@ static TypeInfo apc_info = { .class_init = apc_class_init, }; -static void slavio_misc_register_devices(void) +static void slavio_misc_register_types(void) { type_register_static(&slavio_misc_info); type_register_static(&apc_info); } -device_init(slavio_misc_register_devices) +type_init(slavio_misc_register_types) diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c index 3878f6fa5e..97edebb3ba 100644 --- a/hw/slavio_timer.c +++ b/hw/slavio_timer.c @@ -427,9 +427,9 @@ static TypeInfo slavio_timer_info = { .class_init = slavio_timer_class_init, }; -static void slavio_timer_register_devices(void) +static void slavio_timer_register_types(void) { type_register_static(&slavio_timer_info); } -device_init(slavio_timer_register_devices) +type_init(slavio_timer_register_types) diff --git a/hw/smbus.c b/hw/smbus.c index 77626f3645..e3cf6a2cc8 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -327,9 +327,9 @@ static TypeInfo smbus_device_type_info = { .class_init = smbus_device_class_init, }; -static void smbus_device_register_devices(void) +static void smbus_device_register_types(void) { type_register_static(&smbus_device_type_info); } -device_init(smbus_device_register_devices); +type_init(smbus_device_register_types) diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c index 9d96cbe41a..11adab01b8 100644 --- a/hw/smbus_eeprom.c +++ b/hw/smbus_eeprom.c @@ -130,12 +130,12 @@ static TypeInfo smbus_eeprom_info = { .class_init = smbus_eeprom_class_initfn, }; -static void smbus_eeprom_register_devices(void) +static void smbus_eeprom_register_types(void) { type_register_static(&smbus_eeprom_info); } -device_init(smbus_eeprom_register_devices) +type_init(smbus_eeprom_register_types) void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int eeprom_spd_size) diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 1bf2901d1a..1a5213fa56 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -781,7 +781,7 @@ static TypeInfo smc91c111_info = { .class_init = smc91c111_class_init, }; -static void smc91c111_register_devices(void) +static void smc91c111_register_types(void) { type_register_static(&smc91c111_info); } @@ -802,4 +802,4 @@ void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq) sysbus_connect_irq(s, 0, irq); } -device_init(smc91c111_register_devices) +type_init(smc91c111_register_types) diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 84281be9e2..6ac7384013 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -672,7 +672,7 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, return H_FUNCTION; } -static void hypercall_init(void) +static void hypercall_register_types(void) { /* hcall-pft */ spapr_register_hypercall(H_ENTER, h_enter); @@ -704,4 +704,5 @@ static void hypercall_init(void) /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); } -device_init(hypercall_init); + +type_init(hypercall_register_types) diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 79b394132b..77d40479c8 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -501,7 +501,7 @@ static TypeInfo spapr_vlan_info = { .class_init = spapr_vlan_class_init, }; -static void spapr_vlan_register(void) +static void spapr_vlan_register_types(void) { spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan); spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan); @@ -511,4 +511,5 @@ static void spapr_vlan_register(void) spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl); type_register_static(&spapr_vlan_info); } -device_init(spapr_vlan_register); + +type_init(spapr_vlan_register_types) diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index ed2e4b33e7..cfdd9ddd41 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -242,13 +242,13 @@ static TypeInfo spapr_phb_info = { .class_init = spapr_phb_class_init, }; -static void spapr_register_devices(void) +static void spapr_register_types(void) { type_register_static(&spapr_phb_info); type_register_static(&spapr_main_pci_host_info); } -device_init(spapr_register_devices) +type_init(spapr_register_types) static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, unsigned size) diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index d1ac74cfbd..c0723b3039 100644 --- a/hw/spapr_rtas.c +++ b/hw/spapr_rtas.c @@ -288,7 +288,7 @@ int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr, return 0; } -static void register_core_rtas(void) +static void core_rtas_register_types(void) { spapr_rtas_register("display-character", rtas_display_character); spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); @@ -298,4 +298,5 @@ static void register_core_rtas(void) rtas_query_cpu_stopped_state); spapr_rtas_register("start-cpu", rtas_start_cpu); } -device_init(register_core_rtas); + +type_init(core_rtas_register_types) diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 64f0009814..ea317efbe4 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -778,13 +778,13 @@ static TypeInfo spapr_vio_type_info = { .class_init = vio_spapr_device_class_init, }; -static void spapr_vio_register_devices(void) +static void spapr_vio_register_types(void) { type_register_static(&spapr_vio_bridge_info); type_register_static(&spapr_vio_type_info); } -device_init(spapr_vio_register_devices) +type_init(spapr_vio_register_types) #ifdef CONFIG_FDT static int compare_reg(const void *p1, const void *p2) diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 9cfce19a73..21670170e8 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; @@ -973,8 +973,9 @@ static TypeInfo spapr_vscsi_info = { .class_init = spapr_vscsi_class_init, }; -static void spapr_vscsi_register(void) +static void spapr_vscsi_register_types(void) { type_register_static(&spapr_vscsi_info); } -device_init(spapr_vscsi_register); + +type_init(spapr_vscsi_register_types) diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index a954e7d29b..3efe24211e 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -212,10 +212,11 @@ static VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg) return sdev; } -static void spapr_vty_register(void) +static void spapr_vty_register_types(void) { spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); type_register_static(&spapr_vty_info); } -device_init(spapr_vty_register); + +type_init(spapr_vty_register_types) diff --git a/hw/sparc32_dma.c b/hw/sparc32_dma.c index f07cc6fdf0..1dbf69e808 100644 --- a/hw/sparc32_dma.c +++ b/hw/sparc32_dma.c @@ -307,9 +307,9 @@ static TypeInfo sparc32_dma_info = { .class_init = sparc32_dma_class_init, }; -static void sparc32_dma_register_devices(void) +static void sparc32_dma_register_types(void) { type_register_static(&sparc32_dma_info); } -device_init(sparc32_dma_register_devices) +type_init(sparc32_dma_register_types) diff --git a/hw/spitz.c b/hw/spitz.c index 4e6540d976..1d6d2b0e1b 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -1138,7 +1138,7 @@ static TypeInfo spitz_lcdtg_info = { .class_init = spitz_lcdtg_class_init, }; -static void spitz_register_devices(void) +static void spitz_register_types(void) { type_register_static(&corgi_ssp_info); type_register_static(&spitz_lcdtg_info); @@ -1146,4 +1146,4 @@ static void spitz_register_devices(void) type_register_static(&sl_nand_info); } -device_init(spitz_register_devices) +type_init(spitz_register_types) diff --git a/hw/ssd0303.c b/hw/ssd0303.c index 685602a584..4e1ee6e12b 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -313,9 +313,9 @@ static TypeInfo ssd0303_info = { .class_init = ssd0303_class_init, }; -static void ssd0303_register_devices(void) +static void ssd0303_register_types(void) { type_register_static(&ssd0303_info); } -device_init(ssd0303_register_devices) +type_init(ssd0303_register_types) diff --git a/hw/ssd0323.c b/hw/ssd0323.c index 3c43738ae1..b0b2e94a81 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -355,9 +355,9 @@ static TypeInfo ssd0323_info = { .class_init = ssd0323_class_init, }; -static void ssd03232_register_devices(void) +static void ssd03232_register_types(void) { type_register_static(&ssd0323_info); } -device_init(ssd03232_register_devices) +type_init(ssd03232_register_types) diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index f2e6cecb71..b519bdb29a 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -259,9 +259,9 @@ static TypeInfo ssi_sd_info = { .class_init = ssi_sd_class_init, }; -static void ssi_sd_register_devices(void) +static void ssi_sd_register_types(void) { type_register_static(&ssi_sd_info); } -device_init(ssi_sd_register_devices) +type_init(ssi_sd_register_types) @@ -80,9 +80,9 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val) return ssc->transfer(slave, val); } -static void register_ssi_slave(void) +static void ssi_slave_register_types(void) { type_register_static(&ssi_slave_info); } -device_init(register_ssi_slave); +type_init(ssi_slave_register_types) diff --git a/hw/stellaris.c b/hw/stellaris.c index 31a65cfb77..562fbbf494 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -1451,7 +1451,7 @@ static TypeInfo stellaris_adc_info = { .class_init = stellaris_adc_class_init, }; -static void stellaris_register_devices(void) +static void stellaris_register_types(void) { type_register_static(&stellaris_i2c_info); type_register_static(&stellaris_gptm_info); @@ -1459,4 +1459,4 @@ static void stellaris_register_devices(void) type_register_static(&stellaris_ssi_bus_info); } -device_init(stellaris_register_devices) +type_init(stellaris_register_types) diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 9b1be8dadc..fbe99cb4a9 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -441,9 +441,9 @@ static TypeInfo stellaris_enet_info = { .class_init = stellaris_enet_class_init, }; -static void stellaris_enet_register_devices(void) +static void stellaris_enet_register_types(void) { type_register_static(&stellaris_enet_info); } -device_init(stellaris_enet_register_devices) +type_init(stellaris_enet_register_types) diff --git a/hw/strongarm.c b/hw/strongarm.c index 8d2e7eb6fe..4d5b60fd1f 100644 --- a/hw/strongarm.c +++ b/hw/strongarm.c @@ -1609,7 +1609,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, return s; } -static void strongarm_register_devices(void) +static void strongarm_register_types(void) { type_register_static(&strongarm_pic_info); type_register_static(&strongarm_rtc_sysbus_info); @@ -1618,4 +1618,5 @@ static void strongarm_register_devices(void) type_register_static(&strongarm_uart_info); type_register_static(&strongarm_ssp_info); } -device_init(strongarm_register_devices) + +type_init(strongarm_register_types) diff --git a/hw/sun4c_intctl.c b/hw/sun4c_intctl.c index 081d6cc8bd..8dfa5ecab6 100644 --- a/hw/sun4c_intctl.c +++ b/hw/sun4c_intctl.c @@ -223,9 +223,9 @@ static TypeInfo sun4c_intctl_info = { .class_init = sun4c_intctl_class_init, }; -static void sun4c_intctl_register_devices(void) +static void sun4c_intctl_register_types(void) { type_register_static(&sun4c_intctl_info); } -device_init(sun4c_intctl_register_devices) +type_init(sun4c_intctl_register_types) diff --git a/hw/sun4m.c b/hw/sun4m.c index b79d14c35a..99fb219b3a 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -623,13 +623,6 @@ static TypeInfo idreg_info = { .class_init = idreg_class_init, }; -static void idreg_register_devices(void) -{ - type_register_static(&idreg_info); -} - -device_init(idreg_register_devices); - typedef struct AFXState { SysBusDevice busdev; MemoryRegion mem; @@ -672,13 +665,6 @@ static TypeInfo afx_info = { .class_init = afx_class_init, }; -static void afx_register_devices(void) -{ - type_register_static(&afx_info); -} - -device_init(afx_register_devices); - typedef struct PROMState { SysBusDevice busdev; MemoryRegion prom; @@ -756,13 +742,6 @@ static TypeInfo prom_info = { .class_init = prom_class_init, }; -static void prom_register_devices(void) -{ - type_register_static(&prom_info); -} - -device_init(prom_register_devices); - typedef struct RamDevice { SysBusDevice busdev; @@ -827,13 +806,6 @@ static TypeInfo ram_info = { .class_init = ram_class_init, }; -static void ram_register_devices(void) -{ - type_register_static(&ram_info); -} - -device_init(ram_register_devices); - static void cpu_devinit(const char *cpu_model, unsigned int id, uint64_t prom_addr, qemu_irq **cpu_irqs) { @@ -1865,6 +1837,14 @@ static QEMUMachine ss2_machine = { .use_scsi = 1, }; +static void sun4m_register_types(void) +{ + type_register_static(&idreg_info); + type_register_static(&afx_info); + type_register_static(&prom_info); + type_register_static(&ram_info); +} + static void ss2_machine_init(void) { qemu_register_machine(&ss5_machine); @@ -1881,4 +1861,5 @@ static void ss2_machine_init(void) qemu_register_machine(&ss2_machine); } +type_init(sun4m_register_types) machine_init(ss2_machine_init); diff --git a/hw/sun4m_iommu.c b/hw/sun4m_iommu.c index 727532cb09..ebefa91b7a 100644 --- a/hw/sun4m_iommu.c +++ b/hw/sun4m_iommu.c @@ -380,9 +380,9 @@ static TypeInfo iommu_info = { .class_init = iommu_class_init, }; -static void iommu_register_devices(void) +static void iommu_register_types(void) { type_register_static(&iommu_info); } -device_init(iommu_register_devices) +type_init(iommu_register_types) diff --git a/hw/sun4u.c b/hw/sun4u.c index 79bbd495eb..423108f236 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -580,13 +580,6 @@ static TypeInfo ebus_info = { .class_init = ebus_class_init, }; -static void pci_ebus_register(void) -{ - type_register_static(&ebus_info); -} - -device_init(pci_ebus_register); - typedef struct PROMState { SysBusDevice busdev; MemoryRegion prom; @@ -664,13 +657,6 @@ static TypeInfo prom_info = { .class_init = prom_class_init, }; -static void prom_register_devices(void) -{ - type_register_static(&prom_info); -} - -device_init(prom_register_devices); - typedef struct RamDevice { @@ -728,13 +714,6 @@ static TypeInfo ram_info = { .class_init = ram_class_init, }; -static void ram_register_devices(void) -{ - type_register_static(&ram_info); -} - -device_init(ram_register_devices); - static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) { CPUState *env; @@ -957,6 +936,13 @@ static QEMUMachine niagara_machine = { .max_cpus = 1, // XXX for now }; +static void sun4u_register_types(void) +{ + type_register_static(&ebus_info); + type_register_static(&prom_info); + type_register_static(&ram_info); +} + static void sun4u_machine_init(void) { qemu_register_machine(&sun4u_machine); @@ -964,4 +950,5 @@ static void sun4u_machine_init(void) qemu_register_machine(&niagara_machine); } +type_init(sun4u_register_types) machine_init(sun4u_machine_init); diff --git a/hw/sysbus.c b/hw/sysbus.c index 282060abb6..db4efccef4 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -256,9 +256,9 @@ static TypeInfo sysbus_device_type_info = { .class_init = sysbus_device_class_init, }; -static void sysbus_register(void) +static void sysbus_register_types(void) { type_register_static(&sysbus_device_type_info); } -device_init(sysbus_register); +type_init(sysbus_register_types) @@ -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; @@ -664,9 +664,9 @@ static TypeInfo tcx_info = { .class_init = tcx_class_init, }; -static void tcx_register_devices(void) +static void tcx_register_types(void) { type_register_static(&tcx_info); } -device_init(tcx_register_devices) +type_init(tcx_register_types) diff --git a/hw/tmp105.c b/hw/tmp105.c index a3bdd91d40..8e8dbd94eb 100644 --- a/hw/tmp105.c +++ b/hw/tmp105.c @@ -245,9 +245,9 @@ static TypeInfo tmp105_info = { .class_init = tmp105_class_init, }; -static void tmp105_register_devices(void) +static void tmp105_register_types(void) { type_register_static(&tmp105_info); } -device_init(tmp105_register_devices) +type_init(tmp105_register_types) @@ -291,10 +291,10 @@ static TypeInfo tosa_ssp_info = { .class_init = tosa_ssp_class_init, }; -static void tosa_register_devices(void) +static void tosa_register_types(void) { type_register_static(&tosa_dac_info); type_register_static(&tosa_ssp_info); } -device_init(tosa_register_devices) +type_init(tosa_register_types) diff --git a/hw/tusb6010.c b/hw/tusb6010.c index 0ade670906..5ba8da6d6a 100644 --- a/hw/tusb6010.c +++ b/hw/tusb6010.c @@ -805,9 +805,9 @@ static TypeInfo tusb6010_info = { .class_init = tusb6010_class_init, }; -static void tusb6010_register_device(void) +static void tusb6010_register_types(void) { type_register_static(&tusb6010_info); } -device_init(tusb6010_register_device) +type_init(tusb6010_register_types) diff --git a/hw/twl92230.c b/hw/twl92230.c index 03fdccc8ce..22da6f8001 100644 --- a/hw/twl92230.c +++ b/hw/twl92230.c @@ -61,9 +61,7 @@ typedef struct { } rtc; uint16_t rtc_next_vmstate; qemu_irq out[4]; - qemu_irq *in; uint8_t pwrbtn_state; - qemu_irq pwrbtn; } MenelausState; static inline void menelaus_update(MenelausState *s) @@ -186,14 +184,12 @@ static void menelaus_gpio_set(void *opaque, int line, int level) { MenelausState *s = (MenelausState *) opaque; - /* No interrupt generated */ - s->inputs &= ~(1 << line); - s->inputs |= level << line; -} - -static void menelaus_pwrbtn_set(void *opaque, int line, int level) -{ - MenelausState *s = (MenelausState *) opaque; + if (line < 3) { + /* No interrupt generated */ + s->inputs &= ~(1 << line); + s->inputs |= level << line; + return; + } if (!s->pwrbtn_state && level) { s->status |= 1 << 11; /* PSHBTN */ @@ -849,8 +845,9 @@ static int twl92230_init(I2CSlave *i2c) s->rtc.hz_tm = qemu_new_timer_ms(rt_clock, menelaus_rtc_hz, s); /* Three output pins plus one interrupt pin. */ qdev_init_gpio_out(&i2c->qdev, s->out, 4); - qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 3); - s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0]; + + /* Three input pins plus one power-button pin. */ + qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 4); menelaus_reset(&s->i2c); @@ -876,9 +873,9 @@ static TypeInfo twl92230_info = { .class_init = twl92230_class_init, }; -static void twl92230_register_devices(void) +static void twl92230_register_types(void) { type_register_static(&twl92230_info); } -device_init(twl92230_register_devices) +type_init(twl92230_register_types) diff --git a/hw/unin_pci.c b/hw/unin_pci.c index 17d86aac68..409bcd4cc6 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -467,7 +467,7 @@ static TypeInfo pci_unin_internal_info = { .class_init = pci_unin_internal_class_init, }; -static void unin_register_devices(void) +static void unin_register_types(void) { type_register_static(&unin_main_pci_host_info); type_register_static(&u3_agp_pci_host_info); @@ -480,4 +480,4 @@ static void unin_register_devices(void) type_register_static(&pci_unin_internal_info); } -device_init(unin_register_devices) +type_init(unin_register_types) diff --git a/hw/usb-audio.c b/hw/usb-audio.c index cd589b718a..fed136117b 100644 --- a/hw/usb-audio.c +++ b/hw/usb-audio.c @@ -607,7 +607,7 @@ static int usb_audio_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_OUT: - switch (p->devep) { + switch (p->ep->nr) { case 1: ret = usb_audio_handle_dataout(s, p); break; @@ -624,7 +624,7 @@ fail: if (ret == USB_RET_STALL && s->debug) { fprintf(stderr, "usb-audio: failed data transaction: " "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->devep, p->iov.size); + p->pid, p->ep->nr, p->iov.size); } return ret; } @@ -691,7 +691,6 @@ static void usb_audio_class_init(ObjectClass *klass, void *data) k->product_desc = "QEMU USB Audio Interface"; k->usb_desc = &desc_audio; k->init = usb_audio_initfn; - k->handle_packet = usb_generic_handle_packet; k->handle_reset = usb_audio_handle_reset; k->handle_control = usb_audio_handle_control; k->handle_data = usb_audio_handle_data; @@ -706,10 +705,10 @@ static TypeInfo usb_audio_info = { .class_init = usb_audio_class_init, }; -static void usb_audio_register_devices(void) +static void usb_audio_register_types(void) { type_register_static(&usb_audio_info); usb_legacy_register("usb-audio", "audio", NULL); } -device_init(usb_audio_register_devices) +type_init(usb_audio_register_types) diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 90c3b0e0eb..23c39ecc23 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -423,7 +423,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case USB_EVT_EP: ret = usb_bt_fifo_dequeue(&s->evt, p); break; @@ -442,7 +442,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_OUT: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case USB_ACL_EP: usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, usb_bt_hci_acl_complete, p); @@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev) return 0; } -USBDevice *usb_bt_init(HCIInfo *hci) +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci) { USBDevice *dev; struct USBBtState *s; if (!hci) return NULL; - dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle"); + dev = usb_create_simple(bus, "usb-bt-dongle"); if (!dev) { return NULL; } @@ -535,7 +535,6 @@ static void usb_bt_class_initfn(ObjectClass *klass, void *data) uc->init = usb_bt_initfn; uc->product_desc = "QEMU BT dongle"; uc->usb_desc = &desc_bluetooth; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_bt_handle_reset; uc->handle_control = usb_bt_handle_control; uc->handle_data = usb_bt_handle_data; @@ -550,8 +549,9 @@ static TypeInfo bt_info = { .class_init = usb_bt_class_initfn, }; -static void usb_bt_register_devices(void) +static void usb_bt_register_types(void) { type_register_static(&bt_info); } -device_init(usb_bt_register_devices) + +type_init(usb_bt_register_types) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index b753834584..70b7ebc086 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -74,21 +74,21 @@ static int usb_device_init(USBDevice *dev) return 0; } -static void usb_device_handle_destroy(USBDevice *dev) +USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr) { USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_destroy) { - klass->handle_destroy(dev); + if (klass->find_device) { + return klass->find_device(dev, addr); } + return NULL; } -int usb_device_handle_packet(USBDevice *dev, USBPacket *p) +static void usb_device_handle_destroy(USBDevice *dev) { USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_packet) { - return klass->handle_packet(dev, p); + if (klass->handle_destroy) { + klass->handle_destroy(dev); } - return -ENOSYS; } void usb_device_cancel_packet(USBDevice *dev, USBPacket *p) @@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory { const char *name; const char *usbdevice_name; - USBDevice *(*usbdevice_init)(const char *params); + USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); } LegacyUSBFactory; static GSList *legacy_usb_factory; void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)) + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)) { if (usbdevice_name) { LegacyUSBFactory *f = g_malloc0(sizeof(*f)); @@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name) { DeviceState *dev; -#if 1 - /* temporary stopgap until all usb is properly qdev-ified */ - if (!bus) { - bus = usb_bus_find(-1); - if (!bus) - return NULL; - error_report("%s: no bus specified, using \"%s\" for \"%s\"", - __FUNCTION__, bus->qbus.name, name); - } -#endif - dev = qdev_create(&bus->qbus, name); return USB_DEVICE(dev); } @@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline) } return usb_create_simple(bus, f->name); } - return f->usbdevice_init(params); + return f->usbdevice_init(bus, params); } static void usb_device_class_init(ObjectClass *klass, void *data) @@ -586,9 +576,9 @@ static TypeInfo usb_device_type_info = { .class_init = usb_device_class_init, }; -static void usb_register_devices(void) +static void usb_register_types(void) { type_register_static(&usb_device_type_info); } -device_init(usb_register_devices); +type_init(usb_register_types) diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 881da3002e..ce01e343c6 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -267,6 +267,7 @@ typedef struct CCIDBus { */ typedef struct USBCCIDState { USBDevice dev; + USBEndpoint *intr; CCIDBus bus; CCIDCardState *card; BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */ @@ -446,7 +447,7 @@ static const USBDescDevice desc_device = { { .bNumInterfaces = 1, .bConfigurationValue = 1, - .bmAttributes = 0xa0, + .bmAttributes = 0xe0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface0, @@ -839,7 +840,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full) s->bmSlotICCState |= SLOT_0_CHANGED_MASK; } s->notify_slot_change = true; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } static void ccid_write_data_block_error( @@ -995,7 +996,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_IN: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case CCID_BULK_IN_EP: if (!p->iov.size) { ret = USB_RET_NAK; @@ -1190,6 +1191,7 @@ static int ccid_initfn(USBDevice *dev) usb_desc_init(dev); qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL); + s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); s->bus.qbus.allow_hotplug = 1; s->card = NULL; s->migration_state = MIGRATION_NONE; @@ -1320,7 +1322,6 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) uc->init = ccid_initfn; uc->product_desc = "QEMU USB CCID"; uc->usb_desc = &desc_ccid; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = ccid_handle_reset; uc->handle_control = ccid_handle_control; uc->handle_data = ccid_handle_data; @@ -1354,10 +1355,11 @@ static TypeInfo ccid_card_type_info = { .class_init = ccid_card_class_init, }; -static void ccid_register_devices(void) +static void ccid_register_types(void) { type_register_static(&ccid_card_type_info); type_register_static(&ccid_info); usb_legacy_register(CCID_DEV_NAME, "ccid", NULL); } -device_init(ccid_register_devices) + +type_init(ccid_register_types) diff --git a/hw/usb-desc.c b/hw/usb-desc.c index 3c3ed6a802..ccf85ade9e 100644 --- a/hw/usb-desc.c +++ b/hw/usb-desc.c @@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = dev->config->bConfigurationValue; + /* + * 9.4.2: 0 should be returned if the device is unconfigured, otherwise + * the non zero value of bConfigurationValue. + */ + data[0] = dev->config ? dev->config->bConfigurationValue : 0; ret = 1; break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: @@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, trace_usb_set_config(dev->addr, value, ret); break; - case DeviceRequest | USB_REQ_GET_STATUS: + case DeviceRequest | USB_REQ_GET_STATUS: { + const USBDescConfig *config = dev->config ? + dev->config : &dev->device->confs[0]; + data[0] = 0; - if (dev->config->bmAttributes & 0x40) { + /* + * Default state: Device behavior when this request is received while + * the device is in the Default state is not specified. + * We return the same value that a configured device would return if + * it used the first configuration. + */ + if (config->bmAttributes & 0x40) { data[0] |= 1 << USB_DEVICE_SELF_POWERED; } if (dev->remote_wakeup) { @@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, data[1] = 0x00; ret = 2; break; + } case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 0; diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 75ef71e69e..afc8ccf458 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -715,8 +715,8 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev) EHCIQueue *q, *tmp; QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { - if (q->packet.owner == NULL || - q->packet.owner->dev != dev) { + if (!usb_packet_is_inflight(&q->packet) || + q->packet.ep->dev != dev) { continue; } ehci_free_queue(q); @@ -765,6 +765,11 @@ static void ehci_detach(USBPort *port) USBPort *companion = s->companion_ports[port->index]; companion->ops->detach(companion); companion->dev = NULL; + /* + * EHCI spec 4.2.2: "When a disconnect occurs... On the event, + * the port ownership is returned immediately to the EHCI controller." + */ + *portsc &= ~PORTSC_POWNER; return; } @@ -845,6 +850,26 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], return 0; } +static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) +{ + USBDevice *dev; + USBPort *port; + int i; + + for (i = 0; i < NB_PORTS; i++) { + port = &ehci->ports[i]; + if (!(ehci->portsc[i] & PORTSC_PED)) { + DPRINTF("Port %d not enabled\n", i); + continue; + } + dev = usb_find_device(port, addr); + if (dev != NULL) { + return dev; + } + } + return NULL; +} + /* 4.1 host controller initialization */ static void ehci_reset(void *opaque) { @@ -883,10 +908,11 @@ static void ehci_reset(void *opaque) } if (devs[i] && devs[i]->attached) { usb_attach(&s->ports[i]); - usb_send_msg(devs[i], USB_MSG_RESET); + usb_device_reset(devs[i]); } } ehci_queues_rip_all(s); + qemu_del_timer(s->frame_timer); } static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) @@ -982,7 +1008,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 0); if (dev && dev->attached) { - usb_reset(&s->ports[port]); + usb_port_reset(&s->ports[port]); *portsc &= ~PORTSC_CSC; } @@ -1045,7 +1071,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) if (val & USBCMD_HCRESET) { ehci_reset(s); - val &= ~USBCMD_HCRESET; + val = s->usbcmd; } /* not supporting dynamic frame list size at the moment */ @@ -1331,10 +1357,9 @@ err: static int ehci_execute(EHCIQueue *q) { - USBPort *port; USBDevice *dev; + USBEndpoint *ep; int ret; - int i; int endp; int devadr; @@ -1364,33 +1389,18 @@ static int ehci_execute(EHCIQueue *q) endp = get_field(q->qh.epchar, QH_EPCHAR_EP); devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); - ret = USB_RET_NODEV; + /* TODO: associating device with ehci port */ + dev = ehci_find_device(q->ehci, devadr); + ep = usb_ep_get(dev, q->pid, endp); - usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_setup(&q->packet, q->pid, ep); usb_packet_map(&q->packet, &q->sgl); - // TO-DO: associating device with ehci port - for(i = 0; i < NB_PORTS; i++) { - port = &q->ehci->ports[i]; - dev = port->dev; - - if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { - DPRINTF("Port %d, no exec, not connected(%08X)\n", - i, q->ehci->portsc[i]); - continue; - } - - ret = usb_handle_packet(dev, &q->packet); - - DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " - "(total %d) endp %x ret %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.iov.size, q->tbytes, endp, ret); - - if (ret != USB_RET_NODEV) { - break; - } - } + ret = usb_handle_packet(dev, &q->packet); + DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " + "(total %d) endp %x ret %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, q->pid, + q->packet.iov.size, q->tbytes, endp, ret); if (ret > BUFF_SIZE) { fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); @@ -1406,10 +1416,10 @@ static int ehci_execute(EHCIQueue *q) static int ehci_process_itd(EHCIState *ehci, EHCIitd *itd) { - USBPort *port; USBDevice *dev; + USBEndpoint *ep; int ret; - uint32_t i, j, len, pid, dir, devaddr, endp; + uint32_t i, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); @@ -1447,58 +1457,24 @@ static int ehci_process_itd(EHCIState *ehci, pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); - usb_packet_map(&ehci->ipacket, &ehci->isgl); - - ret = USB_RET_NODEV; - for (j = 0; j < NB_PORTS; j++) { - port = &ehci->ports[j]; - dev = port->dev; - - if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { - continue; - } - + dev = ehci_find_device(ehci, devaddr); + ep = usb_ep_get(dev, pid, endp); + if (ep->type == USB_ENDPOINT_XFER_ISOC) { + usb_packet_setup(&ehci->ipacket, pid, ep); + usb_packet_map(&ehci->ipacket, &ehci->isgl); ret = usb_handle_packet(dev, &ehci->ipacket); - - if (ret != USB_RET_NODEV) { - break; - } + assert(ret != USB_RET_ASYNC); + usb_packet_unmap(&ehci->ipacket); + } else { + DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); + ret = USB_RET_NAK; } - - usb_packet_unmap(&ehci->ipacket); qemu_sglist_destroy(&ehci->isgl); -#if 0 - /* In isoch, there is no facility to indicate a NAK so let's - * instead just complete a zero-byte transaction. Setting - * DBERR seems too draconian. - */ - - if (ret == USB_RET_NAK) { - if (ehci->isoch_pause > 0) { - DPRINTF("ISOCH: received a NAK but paused so returning\n"); - ehci->isoch_pause--; - return 0; - } else if (ehci->isoch_pause == -1) { - DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); - // Pause frindex for up to 50 msec waiting for data from - // remote - ehci->isoch_pause = 50; - return 0; - } else { - DPRINTF("ISOCH: isoch pause timeout! return 0\n"); - ret = 0; - } - } else { - DPRINTF("ISOCH: received ACK, clearing pause\n"); - ehci->isoch_pause = -1; - } -#else if (ret == USB_RET_NAK) { + /* no data for us, so do a zero-length transfer */ ret = 0; } -#endif if (ret >= 0) { if (!dir) { @@ -1508,11 +1484,27 @@ static int ehci_process_itd(EHCIState *ehci, /* IN */ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } - - if (itd->transact[i] & ITD_XACT_IOC) { - ehci_record_interrupt(ehci, USBSTS_INT); + } else { + switch (ret) { + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", ret); + /* Fall through */ + case USB_RET_NODEV: + /* 3.3.2: XACTERR is only allowed on IN transactions */ + if (dir) { + itd->transact[i] |= ITD_XACT_XACTERR; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + } + break; + case USB_RET_BABBLE: + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; } } + if (itd->transact[i] & ITD_XACT_IOC) { + ehci_record_interrupt(ehci, USBSTS_INT); + } itd->transact[i] &= ~ITD_XACT_ACTIVE; } } @@ -2371,17 +2363,16 @@ static int usb_ehci_initfn(PCIDevice *dev) memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - fprintf(stderr, "*** EHCI support is under development ***\n"); - return 0; } -static void ehci_register(void) +static void ehci_register_types(void) { type_register_static(&ehci_info); type_register_static(&ich9_ehci_info); } -device_init(ehci_register); + +type_init(ehci_register_types) /* * vim: expandtab ts=4 diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 3c4e45da70..37bca78eca 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -44,6 +44,7 @@ typedef struct USBHIDState { USBDevice dev; + USBEndpoint *intr; HIDState hid; } USBHIDState; @@ -360,7 +361,7 @@ static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - usb_wakeup(&us->dev); + usb_wakeup(us->intr); } static void usb_hid_handle_reset(USBDevice *dev) @@ -463,8 +464,11 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + hid_pointer_activate(hs); + } if (!hid_has_events(hs) && (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; @@ -501,6 +505,7 @@ static int usb_hid_initfn(USBDevice *dev, int kind) USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); usb_desc_init(dev); + us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); hid_init(&us->hid, kind, usb_hid_changed); return 0; } @@ -557,7 +562,6 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_hid_handle_reset; uc->handle_control = usb_hid_handle_control; uc->handle_data = usb_hid_handle_data; @@ -621,7 +625,7 @@ static TypeInfo usb_keyboard_info = { .class_init = usb_keyboard_class_initfn, }; -static void usb_hid_register_devices(void) +static void usb_hid_register_types(void) { type_register_static(&usb_tablet_info); usb_legacy_register("usb-tablet", "tablet", NULL); @@ -630,4 +634,5 @@ static void usb_hid_register_devices(void) type_register_static(&usb_keyboard_info); usb_legacy_register("usb-kbd", "keyboard", NULL); } -device_init(usb_hid_register_devices) + +type_init(usb_hid_register_types) diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 956b020eed..a12856e2e9 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -37,6 +37,7 @@ typedef struct USBHubPort { typedef struct USBHubState { USBDevice dev; + USBEndpoint *intr; USBHubPort ports[NUM_PORTS]; } USBHubState; @@ -163,7 +164,7 @@ static void usb_hub_attach(USBPort *port1) } else { port->wPortStatus &= ~PORT_STAT_LOW_SPEED; } - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } static void usb_hub_detach(USBPort *port1) @@ -171,7 +172,7 @@ static void usb_hub_detach(USBPort *port1) USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); /* Let upstream know the device on this port is gone */ s->dev.port->ops->child_detach(s->dev.port, port1->dev); @@ -199,7 +200,7 @@ static void usb_hub_wakeup(USBPort *port1) if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } } @@ -220,6 +221,26 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet) s->dev.port->ops->complete(s->dev.port, packet); } +static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) +{ + USBHubState *s = DO_UPCAST(USBHubState, dev, dev); + USBHubPort *port; + USBDevice *downstream; + int i; + + for (i = 0; i < NUM_PORTS; i++) { + port = &s->ports[i]; + if (!(port->wPortStatus & PORT_STAT_ENABLE)) { + continue; + } + downstream = usb_find_device(&port->port, addr); + if (downstream != NULL) { + return downstream; + } + } + return NULL; +} + static void usb_hub_handle_reset(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); @@ -305,7 +326,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, break; case PORT_RESET: if (dev && dev->attached) { - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); port->wPortChange |= PORT_STAT_C_RESET; /* set enable bit */ port->wPortStatus |= PORT_STAT_ENABLE; @@ -396,7 +417,7 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) switch(p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { USBHubPort *port; unsigned int status; uint8_t buf[4]; @@ -435,44 +456,6 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) return ret; } -static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p) -{ - USBHubPort *port; - USBDevice *dev; - int i, ret; - - for(i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - dev = port->port.dev; - if (dev && dev->attached && (port->wPortStatus & PORT_STAT_ENABLE)) { - ret = usb_handle_packet(dev, p); - if (ret != USB_RET_NODEV) { - return ret; - } - } - } - return USB_RET_NODEV; -} - -static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p) -{ - USBHubState *s = (USBHubState *)dev; - -#if defined(DEBUG) && 0 - printf("usb_hub: pid=0x%x\n", pid); -#endif - if (dev->state == USB_STATE_DEFAULT && - dev->addr != 0 && - p->devaddr != dev->addr && - (p->pid == USB_TOKEN_SETUP || - p->pid == USB_TOKEN_OUT || - p->pid == USB_TOKEN_IN)) { - /* broadcast the packet to the devices */ - return usb_hub_broadcast_packet(s, p); - } - return usb_generic_handle_packet(dev, p); -} - static void usb_hub_handle_destroy(USBDevice *dev) { USBHubState *s = (USBHubState *)dev; @@ -499,6 +482,7 @@ static int usb_hub_initfn(USBDevice *dev) int i; usb_desc_init(dev); + s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); for (i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; usb_register_port(usb_bus_from_device(dev), @@ -541,7 +525,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) uc->init = usb_hub_initfn; uc->product_desc = "QEMU USB Hub"; uc->usb_desc = &desc_hub; - uc->handle_packet = usb_hub_handle_packet; + uc->find_device = usb_hub_find_device; uc->handle_reset = usb_hub_handle_reset; uc->handle_control = usb_hub_handle_control; uc->handle_data = usb_hub_handle_data; @@ -557,8 +541,9 @@ static TypeInfo hub_info = { .class_init = usb_hub_class_initfn, }; -static void usb_hub_register_devices(void) +static void usb_hub_register_types(void) { type_register_static(&hub_info); } -device_init(usb_hub_register_devices) + +type_init(usb_hub_register_types) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 6153376f3f..c6f08a0313 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; @@ -341,7 +341,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) uint32_t tag; int ret = 0; struct usb_msd_cbw cbw; - uint8_t devep = p->devep; + uint8_t devep = p->ep->nr; switch (p->pid) { case USB_TOKEN_OUT: @@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_msd_init(const char *filename) +static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; char id[8]; @@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename) } /* create guest device */ - dev = usb_create(NULL /* FIXME */, "usb-storage"); + dev = usb_create(bus, "usb-storage"); if (!dev) { return NULL; } @@ -651,7 +651,6 @@ static void usb_msd_class_initfn(ObjectClass *klass, void *data) uc->init = usb_msd_initfn; uc->product_desc = "QEMU USB MSD"; uc->usb_desc = &desc; - uc->handle_packet = usb_generic_handle_packet; uc->cancel_packet = usb_msd_cancel_io; uc->handle_attach = usb_desc_attach; uc->handle_reset = usb_msd_handle_reset; @@ -669,9 +668,10 @@ static TypeInfo msd_info = { .class_init = usb_msd_class_initfn, }; -static void usb_msd_register_devices(void) +static void usb_msd_register_types(void) { type_register_static(&msd_info); usb_legacy_register("usb-storage", "disk", usb_msd_init); } -device_init(usb_msd_register_devices) + +type_init(usb_msd_register_types) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 4f528d25e5..820907a9a9 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -605,6 +605,8 @@ static int musb_timeout(int ttype, int speed, int val) static void musb_packet(MUSBState *s, MUSBEndPoint *ep, int epnum, int pid, int len, USBCallback cb, int dir) { + USBDevice *dev; + USBEndpoint *uep; int ret; int idx = epnum && dir; int ttype; @@ -622,16 +624,14 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->delayed_cb[dir] = cb; /* A wild guess on the FADDR semantics... */ - usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx], - ep->type[idx] & 0xf); + dev = usb_find_device(&s->port, ep->faddr[idx]); + uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); + usb_packet_setup(&ep->packey[dir].p, pid, uep); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; - if (s->port.dev) - ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p); - else - ret = USB_RET_NODEV; + ret = usb_handle_packet(dev, &ep->packey[dir].p); if (ret == USB_RET_ASYNC) { ep->status[dir] = len; @@ -812,8 +812,8 @@ static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) for (ep = 0; ep < 16; ep++) { for (dir = 0; dir < 2; dir++) { - if (s->ep[ep].packey[dir].p.owner == NULL || - s->ep[ep].packey[dir].p.owner->dev != dev) { + if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || + s->ep[ep].packey[dir].p.ep->dev != dev) { continue; } usb_cancel_packet(&s->ep[ep].packey[dir].p); @@ -1310,7 +1310,7 @@ static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) s->power = (value & 0xef) | (s->power & 0x10); /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ if ((value & MGC_M_POWER_RESET) && s->port.dev) { - usb_send_msg(s->port.dev, USB_MSG_RESET); + usb_device_reset(s->port.dev); /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ if ((value & MGC_M_POWER_HSENAB) && s->port.dev->speed == USB_SPEED_HIGH) diff --git a/hw/usb-net.c b/hw/usb-net.c index e2111413b4..22b82017e3 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1210,7 +1210,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) switch(p->pid) { case USB_TOKEN_IN: - switch (p->devep) { + switch (p->ep->nr) { case 1: ret = usb_net_handle_statusin(s, p); break; @@ -1225,7 +1225,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_OUT: - switch (p->devep) { + switch (p->ep->nr) { case 2: ret = usb_net_handle_dataout(s, p); break; @@ -1243,7 +1243,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) if (ret == USB_RET_STALL) fprintf(stderr, "usbnet: failed data transaction: " "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->devep, p->iov.size); + p->pid, p->ep->nr, p->iov.size); return ret; } @@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_net_init(const char *cmdline) +static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) { USBDevice *dev; QemuOpts *opts; @@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline) return NULL; } - dev = usb_create(NULL /* FIXME */, "usb-net"); + dev = usb_create(bus, "usb-net"); if (!dev) { return NULL; } @@ -1398,7 +1398,6 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data) uc->init = usb_net_initfn; uc->product_desc = "QEMU USB Network Interface"; uc->usb_desc = &desc_net; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_net_handle_reset; uc->handle_control = usb_net_handle_control; uc->handle_data = usb_net_handle_data; @@ -1415,9 +1414,10 @@ static TypeInfo net_info = { .class_init = usb_net_class_initfn, }; -static void usb_net_register_devices(void) +static void usb_net_register_types(void) { type_register_static(&net_info); usb_legacy_register("usb-net", "net", usb_net_init); } -device_init(usb_net_register_devices) + +type_init(usb_net_register_types) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 425030f141..7aa19fe781 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -408,6 +408,23 @@ static void ohci_child_detach(USBPort *port1, USBDevice *child) ohci_async_cancel_device(s, child); } +static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) +{ + USBDevice *dev; + int i; + + for (i = 0; i < ohci->num_ports; i++) { + if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) { + continue; + } + dev = usb_find_device(&ohci->rhport[i].port, addr); + if (dev != NULL) { + return dev; + } + } + return NULL; +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -449,7 +466,7 @@ static void ohci_reset(void *opaque) port = &ohci->rhport[i]; port->ctrl = 0; if (port->port.dev && port->port.dev->attached) { - usb_reset(&port->port); + usb_port_reset(&port->port); } } if (ohci->async_td) { @@ -640,6 +657,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, int ret; int i; USBDevice *dev; + USBEndpoint *ep; struct ohci_iso_td iso_td; uint32_t addr; uint16_t starting_frame; @@ -779,20 +797,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (completion) { ret = ohci->usb_packet.result; } else { - ret = USB_RET_NODEV; - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) - continue; - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - ret = usb_handle_packet(dev, &ohci->usb_packet); - if (ret != USB_RET_NODEV) - break; - } - + dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + usb_packet_setup(&ohci->usb_packet, pid, ep); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret == USB_RET_ASYNC) { return 1; } @@ -880,6 +889,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) int ret; int i; USBDevice *dev; + USBEndpoint *ep; struct ohci_td td; uint32_t addr; int flag_r; @@ -972,31 +982,22 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) ohci->async_td = 0; ohci->async_complete = 0; } else { - ret = USB_RET_NODEV; - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) - continue; - - if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ + if (ohci->async_td) { + /* ??? The hardware should allow one active packet per + endpoint. We only allow one active packet per controller. + This should be sufficient as long as devices respond in a + timely manner. + */ #ifdef DEBUG_PACKET - DPRINTF("Too many pending packets\n"); + DPRINTF("Too many pending packets\n"); #endif - return 1; - } - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); - ret = usb_handle_packet(dev, &ohci->usb_packet); - if (ret != USB_RET_NODEV) - break; + return 1; } + dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + usb_packet_setup(&ohci->usb_packet, pid, ep); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); + ret = usb_handle_packet(dev, &ohci->usb_packet); #ifdef DEBUG_PACKET DPRINTF("ret=%d\n", ret); #endif @@ -1435,7 +1436,7 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { DPRINTF("usb-ohci: port %d: RESET\n", portnum); - usb_send_msg(port->port.dev, USB_MSG_RESET); + usb_device_reset(port->port.dev); port->ctrl &= ~OHCI_PORT_PRS; /* ??? Should this also set OHCI_PORT_PESC. */ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; @@ -1708,8 +1709,8 @@ static void ohci_mem_write(void *opaque, static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) { if (ohci->async_td && - ohci->usb_packet.owner != NULL && - ohci->usb_packet.owner->dev == dev) { + usb_packet_is_inflight(&ohci->usb_packet) && + ohci->usb_packet.ep->dev == dev) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } @@ -1886,9 +1887,10 @@ static TypeInfo ohci_sysbus_info = { .class_init = ohci_sysbus_class_init, }; -static void ohci_register(void) +static void ohci_register_types(void) { type_register_static(&ohci_pci_info); type_register_static(&ohci_sysbus_info); } -device_init(ohci_register); + +type_init(ohci_register_types) diff --git a/hw/usb-serial.c b/hw/usb-serial.c index c2cb6d24e8..0aae379b20 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -353,7 +353,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; int i, ret = 0; - uint8_t devep = p->devep; + uint8_t devep = p->ep->nr; struct iovec *iov; uint8_t header[2]; int first_len, len; @@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_serial_init(const char *filename) +static USBDevice *usb_serial_init(USBBus *bus, const char *filename) { USBDevice *dev; CharDriverState *cdrv; @@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-serial"); + dev = usb_create(bus, "usb-serial"); if (!dev) { return NULL; } @@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename) return dev; } -static USBDevice *usb_braille_init(const char *unused) +static USBDevice *usb_braille_init(USBBus *bus, const char *unused) { USBDevice *dev; CharDriverState *cdrv; @@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-braille"); + dev = usb_create(bus, "usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); qdev_init_nofail(&dev->qdev); @@ -583,7 +583,6 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data) uc->init = usb_serial_initfn; uc->product_desc = "QEMU USB Serial"; uc->usb_desc = &desc_serial; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_serial_handle_reset; uc->handle_control = usb_serial_handle_control; uc->handle_data = usb_serial_handle_data; @@ -612,7 +611,6 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data) uc->init = usb_serial_initfn; uc->product_desc = "QEMU USB Braille"; uc->usb_desc = &desc_braille; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_serial_handle_reset; uc->handle_control = usb_serial_handle_control; uc->handle_data = usb_serial_handle_data; @@ -628,11 +626,12 @@ static TypeInfo braille_info = { .class_init = usb_braille_class_initfn, }; -static void usb_serial_register_devices(void) +static void usb_serial_register_types(void) { type_register_static(&serial_info); usb_legacy_register("usb-serial", "serial", usb_serial_init); type_register_static(&braille_info); usb_legacy_register("usb-braille", "braille", usb_braille_init); } -device_init(usb_serial_register_devices) + +type_init(usb_serial_register_types) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index cddcc8927a..70e3881321 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -73,7 +73,7 @@ #define FRAME_TIMER_FREQ 1000 -#define FRAME_MAX_LOOPS 100 +#define FRAME_MAX_LOOPS 256 #define NB_PORTS 2 @@ -94,33 +94,33 @@ static const char *pid2str(int pid) #define DPRINTF(...) #endif -#ifdef DEBUG_DUMP_DATA -static void dump_data(USBPacket *p, int ret) -{ - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); -} -#else -static void dump_data(USBPacket *p, int ret) {} -#endif - typedef struct UHCIState UHCIState; +typedef struct UHCIAsync UHCIAsync; +typedef struct UHCIQueue UHCIQueue; /* * Pending async transaction. * 'packet' must be the first field because completion * handler does "(UHCIAsync *) pkt" cast. */ -typedef struct UHCIAsync { + +struct UHCIAsync { USBPacket packet; QEMUSGList sgl; - UHCIState *uhci; + UHCIQueue *queue; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; - uint32_t token; - int8_t valid; uint8_t isoc; uint8_t done; -} UHCIAsync; +}; + +struct UHCIQueue { + uint32_t token; + UHCIState *uhci; + QTAILQ_ENTRY(UHCIQueue) next; + QTAILQ_HEAD(, UHCIAsync) asyncs; + int8_t valid; +}; typedef struct UHCIPort { USBPort port; @@ -146,7 +146,7 @@ struct UHCIState { uint32_t pending_int_mask; /* Active packets */ - QTAILQ_HEAD(,UHCIAsync) async_pending; + QTAILQ_HEAD(, UHCIQueue) queues; uint8_t num_ports_vmstate; /* Properties */ @@ -166,62 +166,90 @@ typedef struct UHCI_QH { uint32_t el_link; } UHCI_QH; -static UHCIAsync *uhci_async_alloc(UHCIState *s) +static inline int32_t uhci_queue_token(UHCI_TD *td) { - UHCIAsync *async = g_malloc(sizeof(UHCIAsync)); - - memset(&async->packet, 0, sizeof(async->packet)); - async->uhci = s; - async->valid = 0; - async->td = 0; - async->token = 0; - async->done = 0; - async->isoc = 0; + /* covers ep, dev, pid -> identifies the endpoint */ + return td->token & 0x7ffff; +} + +static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td) +{ + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; + + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + return queue; + } + } + + queue = g_new0(UHCIQueue, 1); + queue->uhci = s; + queue->token = token; + QTAILQ_INIT(&queue->asyncs); + QTAILQ_INSERT_HEAD(&s->queues, queue, next); + return queue; +} + +static void uhci_queue_free(UHCIQueue *queue) +{ + UHCIState *s = queue->uhci; + + QTAILQ_REMOVE(&s->queues, queue, next); + g_free(queue); +} + +static UHCIAsync *uhci_async_alloc(UHCIQueue *queue) +{ + UHCIAsync *async = g_new0(UHCIAsync, 1); + + async->queue = queue; usb_packet_init(&async->packet); - pci_dma_sglist_init(&async->sgl, &s->dev, 1); + pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1); return async; } -static void uhci_async_free(UHCIState *s, UHCIAsync *async) +static void uhci_async_free(UHCIAsync *async) { usb_packet_cleanup(&async->packet); qemu_sglist_destroy(&async->sgl); g_free(async); } -static void uhci_async_link(UHCIState *s, UHCIAsync *async) +static void uhci_async_link(UHCIAsync *async) { - QTAILQ_INSERT_HEAD(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); } -static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) +static void uhci_async_unlink(UHCIAsync *async) { - QTAILQ_REMOVE(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_REMOVE(&queue->asyncs, async, next); } -static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) +static void uhci_async_cancel(UHCIAsync *async) { DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n", async->td, async->token, async->done); if (!async->done) usb_cancel_packet(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); } /* * Mark all outstanding async packets as invalid. * This is used for canceling them when TDs are removed by the HCD. */ -static UHCIAsync *uhci_async_validate_begin(UHCIState *s) +static void uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async; + UHCIQueue *queue; - QTAILQ_FOREACH(async, &s->async_pending, next) { - async->valid--; + QTAILQ_FOREACH(queue, &s->queues, next) { + queue->valid--; } - return NULL; } /* @@ -229,77 +257,74 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr, *n; + UHCIQueue *queue, *n; + UHCIAsync *async; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (curr->valid > 0) { + QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { + if (queue->valid > 0) { continue; } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + while (!QTAILQ_EMPTY(&queue->asyncs)) { + async = QTAILQ_FIRST(&queue->asyncs); + uhci_async_unlink(async); + uhci_async_cancel(async); + } + uhci_queue_free(queue); } } static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (curr->packet.owner == NULL || - curr->packet.owner->dev != dev) { - continue; + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + if (!usb_packet_is_inflight(&curr->packet) || + curr->packet.ep->dev != dev) { + continue; + } + uhci_async_unlink(curr); + uhci_async_cancel(curr); } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); } } static void uhci_async_cancel_all(UHCIState *s) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + uhci_async_unlink(curr); + uhci_async_cancel(curr); + } } } -static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) +static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td) { + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; UHCIAsync *async; - UHCIAsync *match = NULL; - int count = 0; - - /* - * We're looking for the best match here. ie both td addr and token. - * Otherwise we return last good match. ie just token. - * It's ok to match just token because it identifies the transaction - * rather well, token includes: device addr, endpoint, size, etc. - * - * Also since we queue async transactions in reverse order by returning - * last good match we restores the order. - * - * It's expected that we wont have a ton of outstanding transactions. - * If we ever do we'd want to optimize this algorithm. - */ - - QTAILQ_FOREACH(async, &s->async_pending, next) { - if (async->token == token) { - /* Good match */ - match = async; - if (async->td == addr) { - /* Best match */ - break; - } + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + break; } - count++; + } + if (queue == NULL) { + return NULL; } - if (count > 64) - fprintf(stderr, "uhci: warning lots of async transactions\n"); + QTAILQ_FOREACH(async, &queue->asyncs, next) { + if (async->td == addr) { + return async; + } + } - return match; + return NULL; } static void uhci_update_irq(UHCIState *s) @@ -342,7 +367,7 @@ static void uhci_reset(void *opaque) port = &s->ports[i]; port->ctrl = 0x0080; if (port->port.dev && port->port.dev->attached) { - usb_reset(&port->port); + usb_port_reset(&port->port); } } @@ -440,16 +465,12 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) } if (val & UHCI_CMD_GRESET) { UHCIPort *port; - USBDevice *dev; int i; /* send reset on the USB bus */ for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; - dev = port->port.dev; - if (dev && dev->attached) { - usb_send_msg(dev, USB_MSG_RESET); - } + usb_device_reset(port->port.dev); } uhci_reset(s); return; @@ -491,7 +512,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) /* port reset */ if ( (val & UHCI_PORT_RESET) && !(port->ctrl & UHCI_PORT_RESET) ) { - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); } } port->ctrl &= UHCI_PORT_READ_ONLY; @@ -647,30 +668,22 @@ static void uhci_wakeup(USBPort *port1) } } -static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) +static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) { - int i, ret; - - DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", - pid2str(p->pid), p->devaddr, p->devep, p->iov.size); - if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p, 0); + USBDevice *dev; + int i; - ret = USB_RET_NODEV; - for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { + for (i = 0; i < NB_PORTS; i++) { UHCIPort *port = &s->ports[i]; - USBDevice *dev = port->port.dev; - - if (dev && dev->attached && (port->ctrl & UHCI_PORT_EN)) { - ret = usb_handle_packet(dev, p); + if (!(port->ctrl & UHCI_PORT_EN)) { + continue; + } + dev = usb_find_device(&port->port, addr); + if (dev != NULL) { + return dev; } } - - DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); - if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p, ret); - - return ret; + return NULL; } static void uhci_async_complete(USBPort *port, USBPacket *packet); @@ -780,79 +793,69 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in { UHCIAsync *async; int len = 0, max_len; - uint8_t pid, isoc; - uint32_t token; + uint8_t pid; + USBDevice *dev; + USBEndpoint *ep; /* Is active ? */ if (!(td->ctrl & TD_CTRL_ACTIVE)) return 1; - /* token field is not unique for isochronous requests, - * so use the destination buffer - */ - if (td->ctrl & TD_CTRL_IOS) { - token = td->buffer; - isoc = 1; - } else { - token = td->token; - isoc = 0; - } - - async = uhci_async_find_td(s, addr, token); + async = uhci_async_find_td(s, addr, td); if (async) { /* Already submitted */ - async->valid = 32; + async->queue->valid = 32; if (!async->done) return 1; - uhci_async_unlink(s, async); + uhci_async_unlink(async); goto done; } /* Allocate new packet */ - async = uhci_async_alloc(s); + async = uhci_async_alloc(uhci_queue_get(s, td)); if (!async) return 1; /* valid needs to be large enough to handle 10 frame delay * for initial isochronous requests */ - async->valid = 32; + async->queue->valid = 32; async->td = addr; - async->token = token; - async->isoc = isoc; + async->isoc = td->ctrl & TD_CTRL_IOS; max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, - (td->token >> 15) & 0xf); + dev = uhci_find_device(s, (td->token >> 8) & 0x7f); + ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + usb_packet_setup(&async->packet, pid, ep); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - len = uhci_broadcast_packet(s, &async->packet); + len = usb_handle_packet(dev, &async->packet); if (len >= 0) len = max_len; break; case USB_TOKEN_IN: - len = uhci_broadcast_packet(s, &async->packet); + len = usb_handle_packet(dev, &async->packet); break; default: /* invalid pid : frame interrupted */ - uhci_async_free(s, async); + uhci_async_free(async); s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); return -1; } if (len == USB_RET_ASYNC) { - uhci_async_link(s, async); + uhci_async_link(async); return 2; } @@ -861,14 +864,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in done: len = uhci_complete_td(s, td, async, int_mask); usb_packet_unmap(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); return len; } static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); - UHCIState *s = async->uhci; + UHCIState *s = async->queue->uhci; DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); @@ -883,14 +886,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); - uhci_async_unlink(s, async); + uhci_async_unlink(async); uhci_complete_td(s, &td, async, &int_mask); s->pending_int_mask |= int_mask; /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); - uhci_async_free(s, async); + uhci_async_free(async); } else { async->done = 1; uhci_process_frame(s); @@ -939,10 +942,38 @@ static int qhdb_insert(QhDb *db, uint32_t addr) return 0; } +static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) +{ + uint32_t int_mask = 0; + uint32_t plink = td->link; + uint32_t token = uhci_queue_token(td); + UHCI_TD ptd; + int ret; + + fprintf(stderr, "%s: -- %x\n", __func__, token); + while (is_valid(plink)) { + pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd)); + le32_to_cpus(&ptd.link); + le32_to_cpus(&ptd.ctrl); + le32_to_cpus(&ptd.token); + le32_to_cpus(&ptd.buffer); + if (!(ptd.ctrl & TD_CTRL_ACTIVE)) { + break; + } + if (uhci_queue_token(&ptd) != token) { + break; + } + ret = uhci_handle_td(s, plink, &ptd, &int_mask); + assert(ret == 2); /* got USB_RET_ASYNC */ + assert(int_mask == 0); + plink = ptd.link; + } +} + static void uhci_process_frame(UHCIState *s) { uint32_t frame_addr, link, old_td_ctrl, val, int_mask; - uint32_t curr_qh; + uint32_t curr_qh, td_count = 0, bytes_count = 0; int cnt, ret; UHCI_TD td; UHCI_QH qh; @@ -967,13 +998,26 @@ static void uhci_process_frame(UHCIState *s) if (qhdb_insert(&qhdb, link)) { /* * We're going in circles. Which is not a bug because - * HCD is allowed to do that as part of the BW management. - * In our case though it makes no sense to spin here. Sync transations - * are already done, and async completion handler will re-process - * the frame when something is ready. + * HCD is allowed to do that as part of the BW management. + * + * Stop processing here if + * (a) no transaction has been done since we've been + * here last time, or + * (b) we've reached the usb 1.1 bandwidth, which is + * 1280 bytes/frame. */ DPRINTF("uhci: detected loop. qh 0x%x\n", link); - break; + if (td_count == 0) { + DPRINTF("uhci: no transaction last round, stop\n"); + break; + } else if (bytes_count >= 1280) { + DPRINTF("uhci: bandwidth limit reached, stop\n"); + break; + } else { + td_count = 0; + qhdb_reset(&qhdb); + qhdb_insert(&qhdb, link); + } } pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); @@ -1013,47 +1057,62 @@ static void uhci_process_frame(UHCIState *s) pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); } - if (ret < 0) { - /* interrupted frame */ - break; - } - - if (ret == 2 || ret == 1) { - DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, ret == 2 ? "pend" : "skip", - td.link, td.ctrl, td.token, curr_qh); + switch (ret) { + case -1: /* interrupted frame */ + goto out; + case 1: /* goto next queue */ + DPRINTF("uhci: TD 0x%x skip. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); link = curr_qh ? qh.link : td.link; continue; - } - /* completed TD */ + case 2: /* got USB_RET_ASYNC */ + DPRINTF("uhci: TD 0x%x async. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); + if (is_valid(td.link)) { + uhci_fill_queue(s, &td); + } + link = curr_qh ? qh.link : td.link; + continue; - DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, td.link, td.ctrl, td.token, curr_qh); + case 0: /* completed TD */ + DPRINTF("uhci: TD 0x%x done. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); - link = td.link; + link = td.link; + td_count++; + bytes_count += (td.ctrl & 0x7ff) + 1; - if (curr_qh) { - /* update QH element link */ - qh.el_link = link; - val = cpu_to_le32(qh.el_link); - pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); + if (curr_qh) { + /* update QH element link */ + qh.el_link = link; + val = cpu_to_le32(qh.el_link); + pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); - if (!depth_first(link)) { - /* done with this QH */ + if (!depth_first(link)) { + /* done with this QH */ - DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", - curr_qh, qh.link, qh.el_link); + DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", + curr_qh, qh.link, qh.el_link); - curr_qh = 0; - link = qh.link; + curr_qh = 0; + link = qh.link; + } } + break; + + default: + assert(!"unknown return code"); } /* go to the next entry */ } +out: s->pending_int_mask |= int_mask; } @@ -1151,7 +1210,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; - QTAILQ_INIT(&s->async_pending); + QTAILQ_INIT(&s->queues); qemu_register_reset(uhci_reset, s); @@ -1321,7 +1380,7 @@ static TypeInfo ich9_uhci3_info = { .class_init = ich9_uhci3_class_init, }; -static void uhci_register(void) +static void uhci_register_types(void) { type_register_static(&piix3_uhci_info); type_register_static(&piix4_uhci_info); @@ -1330,7 +1389,8 @@ static void uhci_register(void) type_register_static(&ich9_uhci2_info); type_register_static(&ich9_uhci3_info); } -device_init(uhci_register); + +type_init(uhci_register_types) void usb_uhci_piix3_init(PCIBus *bus, int devfn) { diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 14de14d5f7..197e2dced5 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -306,7 +306,7 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { if (!(s->changed || s->idle)) return USB_RET_NAK; s->changed = 0; @@ -357,7 +357,6 @@ static void usb_wacom_class_init(ObjectClass *klass, void *data) uc->product_desc = "QEMU PenPartner Tablet"; uc->usb_desc = &desc_wacom; uc->init = usb_wacom_initfn; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_wacom_handle_reset; uc->handle_control = usb_wacom_handle_control; uc->handle_data = usb_wacom_handle_data; @@ -373,9 +372,10 @@ static TypeInfo wacom_info = { .class_init = usb_wacom_class_init, }; -static void usb_wacom_register_devices(void) +static void usb_wacom_register_types(void) { type_register_static(&wacom_info); usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL); } -device_init(usb_wacom_register_devices) + +type_init(usb_wacom_register_types) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 37e887c431..fc5b542d99 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -307,7 +307,8 @@ typedef struct XHCIState XHCIState; typedef struct XHCITransfer { XHCIState *xhci; USBPacket packet; - bool running; + bool running_async; + bool running_retry; bool cancelled; bool complete; bool backgrounded; @@ -338,6 +339,7 @@ typedef struct XHCIEPContext { unsigned int next_xfer; unsigned int comp_xfer; XHCITransfer transfers[TD_QUEUE]; + XHCITransfer *retry; bool bg_running; bool bg_updating; unsigned int next_bg; @@ -420,6 +422,60 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; +#ifdef DEBUG_XHCI +static const char *TRBType_names[] = { + [TRB_RESERVED] = "TRB_RESERVED", + [TR_NORMAL] = "TR_NORMAL", + [TR_SETUP] = "TR_SETUP", + [TR_DATA] = "TR_DATA", + [TR_STATUS] = "TR_STATUS", + [TR_ISOCH] = "TR_ISOCH", + [TR_LINK] = "TR_LINK", + [TR_EVDATA] = "TR_EVDATA", + [TR_NOOP] = "TR_NOOP", + [CR_ENABLE_SLOT] = "CR_ENABLE_SLOT", + [CR_DISABLE_SLOT] = "CR_DISABLE_SLOT", + [CR_ADDRESS_DEVICE] = "CR_ADDRESS_DEVICE", + [CR_CONFIGURE_ENDPOINT] = "CR_CONFIGURE_ENDPOINT", + [CR_EVALUATE_CONTEXT] = "CR_EVALUATE_CONTEXT", + [CR_RESET_ENDPOINT] = "CR_RESET_ENDPOINT", + [CR_STOP_ENDPOINT] = "CR_STOP_ENDPOINT", + [CR_SET_TR_DEQUEUE] = "CR_SET_TR_DEQUEUE", + [CR_RESET_DEVICE] = "CR_RESET_DEVICE", + [CR_FORCE_EVENT] = "CR_FORCE_EVENT", + [CR_NEGOTIATE_BW] = "CR_NEGOTIATE_BW", + [CR_SET_LATENCY_TOLERANCE] = "CR_SET_LATENCY_TOLERANCE", + [CR_GET_PORT_BANDWIDTH] = "CR_GET_PORT_BANDWIDTH", + [CR_FORCE_HEADER] = "CR_FORCE_HEADER", + [CR_NOOP] = "CR_NOOP", + [ER_TRANSFER] = "ER_TRANSFER", + [ER_COMMAND_COMPLETE] = "ER_COMMAND_COMPLETE", + [ER_PORT_STATUS_CHANGE] = "ER_PORT_STATUS_CHANGE", + [ER_BANDWIDTH_REQUEST] = "ER_BANDWIDTH_REQUEST", + [ER_DOORBELL] = "ER_DOORBELL", + [ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER", + [ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION", + [ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP", + [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE", + [CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION", + [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", +}; + +static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) +{ + if (index >= llen || list[index] == NULL) { + return "???"; + } + return list[index]; +} + +static const char *trb_name(XHCITRB *trb) +{ + return lookup_name(TRB_TYPE(*trb), TRBType_names, + ARRAY_SIZE(TRBType_names)); +} +#endif + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); @@ -487,8 +543,9 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) } ev_trb.control = cpu_to_le32(ev_trb.control); - DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x\n", - xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control); + DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n", + xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control, + trb_name(&ev_trb)); addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE); @@ -649,8 +706,9 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, le32_to_cpus(&trb->control); DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: " - "%016" PRIx64 " %08x %08x\n", - ring->dequeue, trb->parameter, trb->status, trb->control); + "%016" PRIx64 " %08x %08x %s\n", + ring->dequeue, trb->parameter, trb->status, trb->control, + trb_name(trb)); if ((trb->control & TRB_C) != ring->ccs) { return 0; @@ -859,12 +917,17 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { XHCITransfer *t = &epctx->transfers[xferi]; - if (t->running) { + if (t->running_async) { + usb_cancel_packet(&t->packet); + t->running_async = 0; t->cancelled = 1; - /* libusb_cancel_transfer(t->usbxfer) */ DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i); killed++; } + if (t->running_retry) { + t->running_retry = 0; + epctx->retry = NULL; + } if (t->backgrounded) { t->backgrounded = 0; } @@ -885,9 +948,10 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_bg; for (i = 0; i < BG_XFERS; i++) { XHCITransfer *t = &epctx->bg_transfers[xferi]; - if (t->running) { + if (t->running_async) { + usb_cancel_packet(&t->packet); + t->running_async = 0; t->cancelled = 1; - /* libusb_cancel_transfer(t->usbxfer); */ DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i); killed++; } @@ -1336,27 +1400,37 @@ static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer, } #endif -static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, int ep) +static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) { - usb_packet_setup(&xfer->packet, - xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT, - xfer->xhci->slots[xfer->slotid-1].devaddr, - ep & 0x7f); + USBEndpoint *ep; + int dir; + + dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep = usb_ep_get(dev, dir, xfer->epid >> 1); + usb_packet_setup(&xfer->packet, dir, ep); usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, xfer->packet.devaddr, xfer->packet.devep); + xfer->packet.pid, dev->addr, ep->nr); return 0; } static int xhci_complete_packet(XHCITransfer *xfer, int ret) { if (ret == USB_RET_ASYNC) { - xfer->running = 1; + xfer->running_async = 1; + xfer->running_retry = 0; + xfer->complete = 0; + xfer->cancelled = 0; + return 0; + } else if (ret == USB_RET_NAK) { + xfer->running_async = 0; + xfer->running_retry = 1; xfer->complete = 0; xfer->cancelled = 0; return 0; } else { - xfer->running = 0; + xfer->running_async = 0; + xfer->running_retry = 0; xfer->complete = 1; } @@ -1385,6 +1459,14 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) return 0; } +static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) +{ + if (!(port->portsc & PORTSC_PED)) { + return NULL; + } + return usb_find_device(&port->port, addr); +} + static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) { XHCITRB *trb_setup, *trb_status; @@ -1444,7 +1526,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->data_length = wLength; port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = port->port.dev; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); if (!dev) { fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, xhci->slots[xfer->slotid-1].port); @@ -1454,7 +1536,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->in_xfer = bmRequestType & USB_DIR_IN; xfer->iso_xfer = false; - xhci_setup_packet(xfer, port, 0); + xhci_setup_packet(xfer, dev); if (!xfer->in_xfer) { xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); } @@ -1463,7 +1545,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) wValue, wIndex, wLength, xfer->data); xhci_complete_packet(xfer, ret); - if (!xfer->running) { + if (!xfer->running_async && !xfer->running_retry) { xhci_kick_ep(xhci, xfer->slotid, xfer->epid); } return 0; @@ -1476,12 +1558,8 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx int ret; DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); - uint8_t ep = xfer->epid>>1; xfer->in_xfer = epctx->type>>2; - if (xfer->in_xfer) { - ep |= 0x80; - } if (xfer->data && xfer->data_alloced < xfer->data_length) { xfer->data_alloced = 0; @@ -1502,14 +1580,14 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx } port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = port->port.dev; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); if (!dev) { fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, xhci->slots[xfer->slotid-1].port); return -1; } - xhci_setup_packet(xfer, port, ep); + xhci_setup_packet(xfer, dev); switch(epctx->type) { case ET_INTR_OUT: @@ -1522,8 +1600,9 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx FIXME(); break; default: - fprintf(stderr, "xhci: unknown or unhandled EP type %d (ep %02x)\n", - epctx->type, ep); + fprintf(stderr, "xhci: unknown or unhandled EP " + "(type %d, in %d, ep %02x)\n", + epctx->type, xfer->in_xfer, xfer->epid); return -1; } @@ -1533,7 +1612,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx ret = usb_handle_packet(dev, &xfer->packet); xhci_complete_packet(xfer, ret); - if (!xfer->running) { + if (!xfer->running_async && !xfer->running_retry) { xhci_kick_ep(xhci, xfer->slotid, xfer->epid); } return 0; @@ -1604,6 +1683,25 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid return; } + if (epctx->retry) { + /* retry nak'ed transfer */ + XHCITransfer *xfer = epctx->retry; + int result; + + DPRINTF("xhci: retry nack'ed transfer ...\n"); + assert(xfer->running_retry); + xhci_setup_packet(xfer, xfer->packet.ep->dev); + result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + if (result == USB_RET_NAK) { + DPRINTF("xhci: ... xfer still nacked\n"); + return; + } + DPRINTF("xhci: ... result %d\n", result); + xhci_complete_packet(xfer, result); + assert(!xfer->running_retry); + epctx->retry = NULL; + } + if (epctx->state == EP_HALTED) { DPRINTF("xhci: ep halted, not running schedule\n"); return; @@ -1613,9 +1711,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid while (1) { XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; - if (xfer->running || xfer->backgrounded) { - DPRINTF("xhci: ep is busy\n"); + if (xfer->running_async || xfer->running_retry || xfer->backgrounded) { + DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n", + epctx->next_xfer, xfer->running_async, + xfer->running_retry, xfer->backgrounded); break; + } else { + DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer); } length = xhci_ring_chain_length(xhci, &epctx->ring); if (length < 0) { @@ -1658,12 +1760,15 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid } } - /* - * Qemu usb can't handle multiple in-flight xfers. - * Also xfers might be finished here already, - * possibly with an error. Stop here for now. - */ - break; + if (epctx->state == EP_HALTED) { + DPRINTF("xhci: ep halted, stopping schedule\n"); + break; + } + if (xfer->running_retry) { + DPRINTF("xhci: xfer nacked, stopping schedule\n"); + epctx->retry = xfer; + break; + } } } @@ -2346,9 +2451,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) /* write-1-to-start bits */ if (val & PORTSC_PR) { DPRINTF("xhci: port %d reset\n", port); - if (xhci->ports[port].port.dev) { - usb_send_msg(xhci->ports[port].port.dev, USB_MSG_RESET); - } + usb_device_reset(xhci->ports[port].port.dev); portsc |= PORTSC_PRC | PORTSC_PED; } xhci->ports[port].portsc = portsc; @@ -2633,6 +2736,26 @@ static void xhci_detach(USBPort *usbport) xhci_update_port(xhci, port, 1); } +static void xhci_wakeup(USBPort *usbport) +{ + XHCIState *xhci = usbport->opaque; + XHCIPort *port = &xhci->ports[usbport->index]; + int nr = port->port.index + 1; + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; + uint32_t pls; + + pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + if (pls != 3) { + return; + } + port->portsc |= 0xf << PORTSC_PLS_SHIFT; + if (port->portsc & PORTSC_PLC) { + return; + } + port->portsc |= PORTSC_PLC; + xhci_event(xhci, &ev); +} + static void xhci_complete(USBPort *port, USBPacket *packet) { XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); @@ -2649,11 +2772,53 @@ static void xhci_child_detach(USBPort *port, USBDevice *child) static USBPortOps xhci_port_ops = { .attach = xhci_attach, .detach = xhci_detach, + .wakeup = xhci_wakeup, .complete = xhci_complete, .child_detach = xhci_child_detach, }; +static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev) +{ + XHCISlot *slot; + int slotid; + + for (slotid = 1; slotid <= MAXSLOTS; slotid++) { + slot = &xhci->slots[slotid-1]; + if (slot->devaddr == dev->addr) { + return slotid; + } + } + return 0; +} + +static int xhci_find_epid(USBEndpoint *ep) +{ + if (ep->nr == 0) { + return 1; + } + if (ep->pid == USB_TOKEN_IN) { + return ep->nr * 2 + 1; + } else { + return ep->nr * 2; + } +} + +static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +{ + XHCIState *xhci = container_of(bus, XHCIState, bus); + int slotid; + + DPRINTF("%s\n", __func__); + slotid = xhci_find_slotid(xhci, ep->dev); + if (slotid == 0 || !xhci->slots[slotid-1].enabled) { + DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); + return; + } + xhci_kick_ep(xhci, slotid, xhci_find_epid(ep)); +} + static USBBusOps xhci_bus_ops = { + .wakeup_endpoint = xhci_wakeup_endpoint, }; static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) @@ -2667,7 +2832,10 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) for (i = 0; i < MAXPORTS; i++) { memset(&xhci->ports[i], 0, sizeof(xhci->ports[i])); usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i, - &xhci_port_ops, USB_SPEED_MASK_HIGH); + &xhci_port_ops, + USB_SPEED_MASK_LOW | + USB_SPEED_MASK_FULL | + USB_SPEED_MASK_HIGH); } for (i = 0; i < MAXSLOTS; i++) { xhci->slots[i].enabled = 0; @@ -2752,8 +2920,9 @@ static TypeInfo xhci_info = { .class_init = xhci_class_init, }; -static void xhci_register(void) +static void xhci_register_types(void) { type_register_static(&xhci_info); } -device_init(xhci_register); + +type_init(xhci_register_types) @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "usb.h" #include "iov.h" +#include "trace.h" void usb_attach(USBPort *port) { @@ -35,7 +36,8 @@ void usb_attach(USBPort *port) assert(dev->attached); assert(dev->state == USB_STATE_NOTATTACHED); port->ops->attach(port); - usb_send_msg(dev, USB_MSG_ATTACH); + dev->state = USB_STATE_ATTACHED; + usb_device_handle_attach(dev); } void usb_detach(USBPort *port) @@ -45,24 +47,41 @@ void usb_detach(USBPort *port) assert(dev != NULL); assert(dev->state != USB_STATE_NOTATTACHED); port->ops->detach(port); - usb_send_msg(dev, USB_MSG_DETACH); + dev->state = USB_STATE_NOTATTACHED; } -void usb_reset(USBPort *port) +void usb_port_reset(USBPort *port) { USBDevice *dev = port->dev; assert(dev != NULL); usb_detach(port); usb_attach(port); - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); } -void usb_wakeup(USBDevice *dev) +void usb_device_reset(USBDevice *dev) { + if (dev == NULL || !dev->attached) { + return; + } + dev->remote_wakeup = 0; + dev->addr = 0; + dev->state = USB_STATE_DEFAULT; + usb_device_handle_reset(dev); +} + +void usb_wakeup(USBEndpoint *ep) +{ + USBDevice *dev = ep->dev; + USBBus *bus = usb_bus_from_device(dev); + if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { dev->port->ops->wakeup(dev->port); } + if (bus->ops->wakeup_endpoint) { + bus->ops->wakeup_endpoint(bus, ep); + } } /**********************/ @@ -128,8 +147,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - if (p->devep != 0) - return usb_device_handle_data(s, p); + assert(p->ep->nr == 0); request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; @@ -175,8 +193,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) static int do_token_out(USBDevice *s, USBPacket *p) { - if (p->devep != 0) - return usb_device_handle_data(s, p); + assert(p->ep->nr == 0); switch(s->setup_state) { case SETUP_STATE_ACK: @@ -209,51 +226,6 @@ static int do_token_out(USBDevice *s, USBPacket *p) } } -/* - * Generic packet handler. - * Called by the HC (host controller). - * - * Returns length of the transaction or one of the USB_RET_XXX codes. - */ -int usb_generic_handle_packet(USBDevice *s, USBPacket *p) -{ - switch(p->pid) { - case USB_MSG_ATTACH: - s->state = USB_STATE_ATTACHED; - usb_device_handle_attach(s); - return 0; - - case USB_MSG_DETACH: - s->state = USB_STATE_NOTATTACHED; - return 0; - - case USB_MSG_RESET: - s->remote_wakeup = 0; - s->addr = 0; - s->state = USB_STATE_DEFAULT; - usb_device_handle_reset(s); - return 0; - } - - /* Rest of the PIDs must match our address */ - if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) - return USB_RET_NODEV; - - switch (p->pid) { - case USB_TOKEN_SETUP: - return do_token_setup(s, p); - - case USB_TOKEN_IN: - return do_token_in(s, p); - - case USB_TOKEN_OUT: - return do_token_out(s, p); - - default: - return USB_RET_STALL; - } -} - /* ctrl complete function for devices which use usb_generic_handle_packet and may return USB_RET_ASYNC from their handle_control callback. Device code which does this *must* call this function instead of the normal @@ -301,17 +273,39 @@ int set_usb_string(uint8_t *buf, const char *str) return q - buf; } -/* Send an internal message to a USB device. */ -void usb_send_msg(USBDevice *dev, int msg) +USBDevice *usb_find_device(USBPort *port, uint8_t addr) { - USBPacket p; - int ret; + USBDevice *dev = port->dev; - memset(&p, 0, sizeof(p)); - p.pid = msg; - ret = usb_handle_packet(dev, &p); - /* This _must_ be synchronous */ - assert(ret != USB_RET_ASYNC); + if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { + return NULL; + } + if (dev->addr == addr) { + return dev; + } + return usb_device_find_device(dev, addr); +} + +static int usb_process_one(USBPacket *p) +{ + USBDevice *dev = p->ep->dev; + + if (p->ep->nr == 0) { + /* control pipe */ + switch (p->pid) { + case USB_TOKEN_SETUP: + return do_token_setup(dev, p); + case USB_TOKEN_IN: + return do_token_in(dev, p); + case USB_TOKEN_OUT: + return do_token_out(dev, p); + default: + return USB_RET_STALL; + } + } else { + /* data pipe */ + return usb_device_handle_data(dev, p); + } } /* Hand over a packet to a device for processing. Return value @@ -321,17 +315,27 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) { int ret; - assert(p->owner == NULL); - ret = usb_device_handle_packet(dev, p); - if (ret == USB_RET_ASYNC) { - if (p->owner == NULL) { - p->owner = usb_ep_get(dev, p->pid, p->devep); + if (dev == NULL) { + return USB_RET_NODEV; + } + assert(dev == p->ep->dev); + assert(dev->state == USB_STATE_DEFAULT); + assert(p->state == USB_PACKET_SETUP); + assert(p->ep != NULL); + + if (QTAILQ_EMPTY(&p->ep->queue)) { + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); } else { - /* We'll end up here when usb_handle_packet is called - * recursively due to a hub being in the chain. Nothing - * to do. Leave p->owner pointing to the device, not the - * hub. */; + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); } + } else { + ret = USB_RET_ASYNC; + usb_packet_set_state(p, USB_PACKET_QUEUED); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); } return ret; } @@ -341,10 +345,28 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) handle_packet. */ void usb_packet_complete(USBDevice *dev, USBPacket *p) { - /* Note: p->owner != dev is possible in case dev is a hub */ - assert(p->owner != NULL); - p->owner = NULL; + USBEndpoint *ep = p->ep; + int ret; + + assert(p->state == USB_PACKET_ASYNC); + assert(QTAILQ_FIRST(&ep->queue) == p); + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); dev->port->ops->complete(dev->port, p); + + while (!QTAILQ_EMPTY(&ep->queue)) { + p = QTAILQ_FIRST(&ep->queue); + assert(p->state == USB_PACKET_QUEUED); + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + break; + } + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); + dev->port->ops->complete(dev->port, p); + } } /* Cancel an active packet. The packed must have been deferred by @@ -352,9 +374,13 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) completed. */ void usb_cancel_packet(USBPacket * p) { - assert(p->owner != NULL); - usb_device_cancel_packet(p->owner->dev, p); - p->owner = NULL; + bool callback = (p->state == USB_PACKET_ASYNC); + assert(usb_packet_is_inflight(p)); + usb_packet_set_state(p, USB_PACKET_CANCELED); + QTAILQ_REMOVE(&p->ep->queue, p, queue); + if (callback) { + usb_device_cancel_packet(p->ep->dev, p); + } } @@ -363,13 +389,32 @@ void usb_packet_init(USBPacket *p) qemu_iovec_init(&p->iov, 1); } -void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) +void usb_packet_set_state(USBPacket *p, USBPacketState state) { + static const char *name[] = { + [USB_PACKET_UNDEFINED] = "undef", + [USB_PACKET_SETUP] = "setup", + [USB_PACKET_QUEUED] = "queued", + [USB_PACKET_ASYNC] = "async", + [USB_PACKET_COMPLETE] = "complete", + [USB_PACKET_CANCELED] = "canceled", + }; + USBDevice *dev = p->ep->dev; + USBBus *bus = usb_bus_from_device(dev); + + trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, + p, name[p->state], name[state]); + p->state = state; +} + +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) +{ + assert(!usb_packet_is_inflight(p)); p->pid = pid; - p->devaddr = addr; - p->devep = ep; + p->ep = ep; p->result = 0; qemu_iovec_reset(&p->iov); + usb_packet_set_state(p, USB_PACKET_SETUP); } void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) @@ -408,6 +453,7 @@ void usb_packet_skip(USBPacket *p, size_t bytes) void usb_packet_cleanup(USBPacket *p) { + assert(!usb_packet_is_inflight(p)); qemu_iovec_destroy(&p->iov); } @@ -415,16 +461,24 @@ void usb_ep_init(USBDevice *dev) { int ep; + dev->ep_ctl.nr = 0; dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.dev = dev; + QTAILQ_INIT(&dev->ep_ctl.queue); for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { + dev->ep_in[ep].nr = ep + 1; + dev->ep_out[ep].nr = ep + 1; + dev->ep_in[ep].pid = USB_TOKEN_IN; + dev->ep_out[ep].pid = USB_TOKEN_OUT; dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; dev->ep_in[ep].ifnum = 0; dev->ep_out[ep].ifnum = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; + QTAILQ_INIT(&dev->ep_in[ep].queue); + QTAILQ_INIT(&dev->ep_out[ep].queue); } } @@ -472,7 +526,12 @@ void usb_ep_dump(USBDevice *dev) struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) { - struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out; + struct USBEndpoint *eps; + + if (dev == NULL) { + return NULL; + } + eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; if (ep == 0) { return &dev->ep_ctl; } @@ -39,11 +39,6 @@ #define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_TOKEN_OUT 0xe1 /* host -> device */ -/* specific usb messages, also sent in the 'pid' parameter */ -#define USB_MSG_ATTACH 0x100 -#define USB_MSG_DETACH 0x101 -#define USB_MSG_RESET 0x102 - #define USB_RET_NODEV (-1) #define USB_RET_NAK (-2) #define USB_RET_STALL (-3) @@ -176,10 +171,13 @@ struct USBDescString { #define USB_MAX_INTERFACES 16 struct USBEndpoint { + uint8_t nr; + uint8_t pid; uint8_t type; uint8_t ifnum; int max_packet_size; USBDevice *dev; + QTAILQ_HEAD(, USBPacket) queue; }; /* definition of a USB device */ @@ -234,13 +232,10 @@ typedef struct USBDeviceClass { int (*init)(USBDevice *dev); /* - * Process USB packet. - * Called by the HC (Host Controller). - * - * Returns length of the transaction - * or one of the USB_RET_XXX codes. + * Walk (enabled) downstream ports, check for a matching device. + * Only hubs implement this. */ - int (*handle_packet)(USBDevice *dev, USBPacket *p); + USBDevice *(*find_device)(USBDevice *dev, uint8_t addr); /* * Called when a packet is canceled. @@ -297,8 +292,7 @@ typedef struct USBPortOps { void (*wakeup)(USBPort *port); /* * Note that port->dev will be different then the device from which - * the packet originated when a hub is involved, if you want the orginating - * device use p->owner + * the packet originated when a hub is involved. */ void (*complete)(USBPort *port, USBPacket *p); } USBPortOps; @@ -316,20 +310,30 @@ struct USBPort { typedef void USBCallback(USBPacket * packet, void *opaque); +typedef enum USBPacketState { + USB_PACKET_UNDEFINED = 0, + USB_PACKET_SETUP, + USB_PACKET_QUEUED, + USB_PACKET_ASYNC, + USB_PACKET_COMPLETE, + USB_PACKET_CANCELED, +} USBPacketState; + /* Structure used to hold information about an active USB packet. */ struct USBPacket { /* Data fields for use by the driver. */ int pid; - uint8_t devaddr; - uint8_t devep; + USBEndpoint *ep; QEMUIOVector iov; int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ - USBEndpoint *owner; + USBPacketState state; + QTAILQ_ENTRY(USBPacket) queue; }; void usb_packet_init(USBPacket *p); -void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep); +void usb_packet_set_state(USBPacket *p, USBPacketState state); +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p); @@ -337,6 +341,14 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); void usb_packet_skip(USBPacket *p, size_t bytes); void usb_packet_cleanup(USBPacket *p); +static inline bool usb_packet_is_inflight(USBPacket *p) +{ + return (p->state == USB_PACKET_QUEUED || + p->state == USB_PACKET_ASYNC); +} + +USBDevice *usb_find_device(USBPort *port, uint8_t addr); + int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_cancel_packet(USBPacket * p); @@ -354,20 +366,19 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); void usb_attach(USBPort *port); void usb_detach(USBPort *port); -void usb_reset(USBPort *port); -void usb_wakeup(USBDevice *dev); -int usb_generic_handle_packet(USBDevice *s, USBPacket *p); +void usb_port_reset(USBPort *port); +void usb_device_reset(USBDevice *dev); +void usb_wakeup(USBEndpoint *ep); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); -void usb_send_msg(USBDevice *dev, int msg); /* usb-linux.c */ -USBDevice *usb_host_device_open(const char *devname); +USBDevice *usb_host_device_open(USBBus *bus, const char *devname); int usb_host_device_close(const char *devname); void usb_host_info(Monitor *mon); /* usb-bt.c */ -USBDevice *usb_bt_init(HCIInfo *hci); +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); /* usb ports of the VM */ @@ -414,12 +425,14 @@ struct USBBus { struct USBBusOps { int (*register_companion)(USBBus *bus, USBPort *ports[], uint32_t portcount, uint32_t firstport); + void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep); }; void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)); + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)); USBDevice *usb_create(USBBus *bus, const char *name); USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); @@ -451,7 +464,7 @@ extern const VMStateDescription vmstate_usb_device; .offset = vmstate_offset_value(_state, _field, USBDevice), \ } -int usb_device_handle_packet(USBDevice *dev, USBPacket *p); +USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr); void usb_device_cancel_packet(USBDevice *dev, USBPacket *p); diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index c4105e977b..ae53a8b37b 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -154,11 +154,11 @@ static TypeInfo pci_realview_info = { .class_init = pci_realview_class_init, }; -static void versatile_pci_register_devices(void) +static void versatile_pci_register_types(void) { type_register_static(&pci_vpb_info); type_register_static(&pci_realview_info); type_register_static(&versatile_pci_host_info); } -device_init(versatile_pci_register_devices) +type_init(versatile_pci_register_types) diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 6ea0ce5fb3..b9102f4a54 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -9,7 +9,6 @@ #include "sysbus.h" #include "arm-misc.h" -#include "primecell.h" #include "devices.h" #include "net.h" #include "sysemu.h" @@ -382,9 +381,9 @@ static TypeInfo vpb_sic_info = { .class_init = vpb_sic_class_init, }; -static void versatilepb_register_devices(void) +static void versatilepb_register_types(void) { type_register_static(&vpb_sic_info); } -device_init(versatilepb_register_devices) +type_init(versatilepb_register_types) diff --git a/hw/vexpress.c b/hw/vexpress.c index 43f47a65ce..b9aafec4cc 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -30,42 +30,152 @@ #include "boards.h" #include "exec-memory.h" -#define SMP_BOOT_ADDR 0xe0000000 -#define SMP_BOOTREG_ADDR 0x10000030 - #define VEXPRESS_BOARD_ID 0x8e0 -static struct arm_boot_info vexpress_binfo = { - .smp_loader_start = SMP_BOOT_ADDR, - .smp_bootreg_addr = SMP_BOOTREG_ADDR, +static struct arm_boot_info vexpress_binfo; + +/* Address maps for peripherals: + * the Versatile Express motherboard has two possible maps, + * the "legacy" one (used for A9) and the "Cortex-A Series" + * map (used for newer cores). + * Individual daughterboards can also have different maps for + * their peripherals. + */ + +enum { + VE_SYSREGS, + VE_SP810, + VE_SERIALPCI, + VE_PL041, + VE_MMCI, + VE_KMI0, + VE_KMI1, + VE_UART0, + VE_UART1, + VE_UART2, + VE_UART3, + VE_WDT, + VE_TIMER01, + VE_TIMER23, + VE_SERIALDVI, + VE_RTC, + VE_COMPACTFLASH, + VE_CLCD, + VE_NORFLASH0, + VE_NORFLASH0ALIAS, + VE_NORFLASH1, + VE_SRAM, + VE_VIDEORAM, + VE_ETHERNET, + VE_USB, + VE_DAPROM, }; -static void vexpress_a9_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename, const char *cpu_model) +static target_phys_addr_t motherboard_legacy_map[] = { + /* CS7: 0x10000000 .. 0x10020000 */ + [VE_SYSREGS] = 0x10000000, + [VE_SP810] = 0x10001000, + [VE_SERIALPCI] = 0x10002000, + [VE_PL041] = 0x10004000, + [VE_MMCI] = 0x10005000, + [VE_KMI0] = 0x10006000, + [VE_KMI1] = 0x10007000, + [VE_UART0] = 0x10009000, + [VE_UART1] = 0x1000a000, + [VE_UART2] = 0x1000b000, + [VE_UART3] = 0x1000c000, + [VE_WDT] = 0x1000f000, + [VE_TIMER01] = 0x10011000, + [VE_TIMER23] = 0x10012000, + [VE_SERIALDVI] = 0x10016000, + [VE_RTC] = 0x10017000, + [VE_COMPACTFLASH] = 0x1001a000, + [VE_CLCD] = 0x1001f000, + /* CS0: 0x40000000 .. 0x44000000 */ + [VE_NORFLASH0] = 0x40000000, + /* CS1: 0x44000000 .. 0x48000000 */ + [VE_NORFLASH1] = 0x44000000, + /* CS2: 0x48000000 .. 0x4a000000 */ + [VE_SRAM] = 0x48000000, + /* CS3: 0x4c000000 .. 0x50000000 */ + [VE_VIDEORAM] = 0x4c000000, + [VE_ETHERNET] = 0x4e000000, + [VE_USB] = 0x4f000000, +}; + +static target_phys_addr_t motherboard_aseries_map[] = { + /* CS0: 0x00000000 .. 0x0c000000 */ + [VE_NORFLASH0] = 0x00000000, + [VE_NORFLASH0ALIAS] = 0x08000000, + /* CS4: 0x0c000000 .. 0x10000000 */ + [VE_NORFLASH1] = 0x0c000000, + /* CS5: 0x10000000 .. 0x14000000 */ + /* CS1: 0x14000000 .. 0x18000000 */ + [VE_SRAM] = 0x14000000, + /* CS2: 0x18000000 .. 0x1c000000 */ + [VE_VIDEORAM] = 0x18000000, + [VE_ETHERNET] = 0x1a000000, + [VE_USB] = 0x1b000000, + /* CS3: 0x1c000000 .. 0x20000000 */ + [VE_DAPROM] = 0x1c000000, + [VE_SYSREGS] = 0x1c010000, + [VE_SP810] = 0x1c020000, + [VE_SERIALPCI] = 0x1c030000, + [VE_PL041] = 0x1c040000, + [VE_MMCI] = 0x1c050000, + [VE_KMI0] = 0x1c060000, + [VE_KMI1] = 0x1c070000, + [VE_UART0] = 0x1c090000, + [VE_UART1] = 0x1c0a0000, + [VE_UART2] = 0x1c0b0000, + [VE_UART3] = 0x1c0c0000, + [VE_WDT] = 0x1c0f0000, + [VE_TIMER01] = 0x1c110000, + [VE_TIMER23] = 0x1c120000, + [VE_SERIALDVI] = 0x1c160000, + [VE_RTC] = 0x1c170000, + [VE_COMPACTFLASH] = 0x1c1a0000, + [VE_CLCD] = 0x1c1f0000, +}; + +/* Structure defining the peculiarities of a specific daughterboard */ + +typedef struct VEDBoardInfo VEDBoardInfo; + +typedef void DBoardInitFn(const VEDBoardInfo *daughterboard, + ram_addr_t ram_size, + const char *cpu_model, + qemu_irq *pic, uint32_t *proc_id); + +struct VEDBoardInfo { + const target_phys_addr_t *motherboard_map; + target_phys_addr_t loader_start; + const target_phys_addr_t gic_cpu_if_addr; + DBoardInitFn *init; +}; + +static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, + ram_addr_t ram_size, + const char *cpu_model, + qemu_irq *pic, uint32_t *proc_id) { CPUState *env = NULL; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *lowram = g_new(MemoryRegion, 1); - MemoryRegion *vram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *hackram = g_new(MemoryRegion, 1); - DeviceState *dev, *sysctl, *pl041; + DeviceState *dev; SysBusDevice *busdev; qemu_irq *irqp; - qemu_irq pic[64]; int n; qemu_irq cpu_irq[4]; - uint32_t proc_id; - uint32_t sys_id; - ram_addr_t low_ram_size, vram_size, sram_size; + ram_addr_t low_ram_size; if (!cpu_model) { cpu_model = "cortex-a9"; } + *proc_id = 0x0c000191; + for (n = 0; n < smp_cpus; n++) { env = cpu_init(cpu_model); if (!env) { @@ -78,7 +188,7 @@ static void vexpress_a9_init(ram_addr_t ram_size, if (ram_size > 0x40000000) { /* 1GB is the maximum the address space permits */ - fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n"); + fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n"); exit(1); } @@ -101,8 +211,7 @@ static void vexpress_a9_init(ram_addr_t ram_size, qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); busdev = sysbus_from_qdev(dev); - vexpress_binfo.smp_priv_base = 0x1e000000; - sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base); + sysbus_mmio_map(busdev, 0, 0x1e000000); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); } @@ -116,104 +225,208 @@ static void vexpress_a9_init(ram_addr_t ram_size, pic[n] = qdev_get_gpio_in(dev, n); } - /* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */ + /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ + + /* 0x10020000 PL111 CLCD (daughterboard) */ + sysbus_create_simple("pl111", 0x10020000, pic[44]); + + /* 0x10060000 AXI RAM */ + /* 0x100e0000 PL341 Dynamic Memory Controller */ + /* 0x100e1000 PL354 Static Memory Controller */ + /* 0x100e2000 System Configuration Controller */ + + sysbus_create_simple("sp804", 0x100e4000, pic[48]); + /* 0x100e5000 SP805 Watchdog module */ + /* 0x100e6000 BP147 TrustZone Protection Controller */ + /* 0x100e9000 PL301 'Fast' AXI matrix */ + /* 0x100ea000 PL301 'Slow' AXI matrix */ + /* 0x100ec000 TrustZone Address Space Controller */ + /* 0x10200000 CoreSight debug APB */ + /* 0x1e00a000 PL310 L2 Cache Controller */ + sysbus_create_varargs("l2x0", 0x1e00a000, NULL); +} + +static const VEDBoardInfo a9_daughterboard = { + .motherboard_map = motherboard_legacy_map, + .loader_start = 0x60000000, + .gic_cpu_if_addr = 0x1e000100, + .init = a9_daughterboard_init, +}; + +static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, + ram_addr_t ram_size, + const char *cpu_model, + qemu_irq *pic, uint32_t *proc_id) +{ + int n; + CPUState *env = NULL; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *sram = g_new(MemoryRegion, 1); + qemu_irq cpu_irq[4]; + DeviceState *dev; + SysBusDevice *busdev; + + if (!cpu_model) { + cpu_model = "cortex-a15"; + } + + *proc_id = 0x14000217; + + for (n = 0; n < smp_cpus; n++) { + qemu_irq *irqp; + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + irqp = arm_pic_init_cpu(env); + cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ]; + } + + if (ram_size > 0x80000000) { + fprintf(stderr, "vexpress-a15: cannot model more than 2GB RAM\n"); + exit(1); + } + + memory_region_init_ram(ram, "vexpress.highmem", ram_size); + vmstate_register_ram_global(ram); + /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */ + memory_region_add_subregion(sysmem, 0x80000000, ram); + + /* 0x2c000000 A15MPCore private memory region (GIC) */ + dev = qdev_create(NULL, "a15mpcore_priv"); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + sysbus_mmio_map(busdev, 0, 0x2c000000); + for (n = 0; n < smp_cpus; n++) { + sysbus_connect_irq(busdev, n, cpu_irq[n]); + } + /* Interrupts [42:0] are from the motherboard; + * [47:43] are reserved; [63:48] are daughterboard + * peripherals. Note that some documentation numbers + * external interrupts starting from 32 (because there + * are internal interrupts 0..31). + */ + for (n = 0; n < 64; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + /* A15 daughterboard peripherals: */ + + /* 0x20000000: CoreSight interfaces: not modelled */ + /* 0x2a000000: PL301 AXI interconnect: not modelled */ + /* 0x2a420000: SCC: not modelled */ + /* 0x2a430000: system counter: not modelled */ + /* 0x2b000000: HDLCD controller: not modelled */ + /* 0x2b060000: SP805 watchdog: not modelled */ + /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */ + /* 0x2e000000: system SRAM */ + memory_region_init_ram(sram, "vexpress.a15sram", 0x10000); + vmstate_register_ram_global(sram); + memory_region_add_subregion(sysmem, 0x2e000000, sram); + + /* 0x7ffb0000: DMA330 DMA controller: not modelled */ + /* 0x7ffd0000: PL354 static memory controller: not modelled */ +} + +static const VEDBoardInfo a15_daughterboard = { + .motherboard_map = motherboard_aseries_map, + .loader_start = 0x80000000, + .gic_cpu_if_addr = 0x2c002000, + .init = a15_daughterboard_init, +}; + +static void vexpress_common_init(const VEDBoardInfo *daughterboard, + ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + DeviceState *dev, *sysctl, *pl041; + qemu_irq pic[64]; + uint32_t proc_id; + uint32_t sys_id; + ram_addr_t vram_size, sram_size; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *vram = g_new(MemoryRegion, 1); + MemoryRegion *sram = g_new(MemoryRegion, 1); + const target_phys_addr_t *map = daughterboard->motherboard_map; + + daughterboard->init(daughterboard, ram_size, cpu_model, pic, &proc_id); + + /* Motherboard peripherals: the wiring is the same but the + * addresses vary between the legacy and A-Series memory maps. + */ + sys_id = 0x1190f500; - proc_id = 0x0c000191; - /* 0x10000000 System registers */ sysctl = qdev_create(NULL, "realview_sysctl"); qdev_prop_set_uint32(sysctl, "sys_id", sys_id); qdev_prop_set_uint32(sysctl, "proc_id", proc_id); qdev_init_nofail(sysctl); - sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); + sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]); + + /* VE_SP810: not modelled */ + /* VE_SERIALPCI: not modelled */ - /* 0x10001000 SP810 system control */ - /* 0x10002000 serial bus PCI */ - /* 0x10004000 PL041 audio */ pl041 = qdev_create(NULL, "pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); qdev_init_nofail(pl041); - sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]); sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]); - dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL); + dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL); /* Wire up MMC card detect and read-only signals */ qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT)); qdev_connect_gpio_out(dev, 1, qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN)); - sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]); - sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]); - - sysbus_create_simple("pl011", 0x10009000, pic[5]); - sysbus_create_simple("pl011", 0x1000a000, pic[6]); - sysbus_create_simple("pl011", 0x1000b000, pic[7]); - sysbus_create_simple("pl011", 0x1000c000, pic[8]); - - /* 0x1000f000 SP805 WDT */ + sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]); + sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]); - sysbus_create_simple("sp804", 0x10011000, pic[2]); - sysbus_create_simple("sp804", 0x10012000, pic[3]); + sysbus_create_simple("pl011", map[VE_UART0], pic[5]); + sysbus_create_simple("pl011", map[VE_UART1], pic[6]); + sysbus_create_simple("pl011", map[VE_UART2], pic[7]); + sysbus_create_simple("pl011", map[VE_UART3], pic[8]); - /* 0x10016000 Serial Bus DVI */ + sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); + sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */ + /* VE_SERIALDVI: not modelled */ - /* 0x1001a000 Compact Flash */ + sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */ - /* 0x1001f000 PL111 CLCD (motherboard) */ + /* VE_COMPACTFLASH: not modelled */ - /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ - - /* 0x10020000 PL111 CLCD (daughterboard) */ - sysbus_create_simple("pl111", 0x10020000, pic[44]); + sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); - /* 0x10060000 AXI RAM */ - /* 0x100e0000 PL341 Dynamic Memory Controller */ - /* 0x100e1000 PL354 Static Memory Controller */ - /* 0x100e2000 System Configuration Controller */ - - sysbus_create_simple("sp804", 0x100e4000, pic[48]); - /* 0x100e5000 SP805 Watchdog module */ - /* 0x100e6000 BP147 TrustZone Protection Controller */ - /* 0x100e9000 PL301 'Fast' AXI matrix */ - /* 0x100ea000 PL301 'Slow' AXI matrix */ - /* 0x100ec000 TrustZone Address Space Controller */ - /* 0x10200000 CoreSight debug APB */ - /* 0x1e00a000 PL310 L2 Cache Controller */ - sysbus_create_varargs("l2x0", 0x1e00a000, NULL); + /* VE_NORFLASH0: not modelled */ + /* VE_NORFLASH0ALIAS: not modelled */ + /* VE_NORFLASH1: not modelled */ - /* CS0: NOR0 flash : 0x40000000 .. 0x44000000 */ - /* CS4: NOR1 flash : 0x44000000 .. 0x48000000 */ - /* CS2: SRAM : 0x48000000 .. 0x4a000000 */ sram_size = 0x2000000; memory_region_init_ram(sram, "vexpress.sram", sram_size); vmstate_register_ram_global(sram); - memory_region_add_subregion(sysmem, 0x48000000, sram); - - /* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */ + memory_region_add_subregion(sysmem, map[VE_SRAM], sram); - /* 0x4c000000 Video RAM */ vram_size = 0x800000; memory_region_init_ram(vram, "vexpress.vram", vram_size); vmstate_register_ram_global(vram); - memory_region_add_subregion(sysmem, 0x4c000000, vram); + memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram); /* 0x4e000000 LAN9118 Ethernet */ if (nd_table[0].vlan) { - lan9118_init(&nd_table[0], 0x4e000000, pic[15]); + lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]); } - /* 0x4f000000 ISP1761 USB */ + /* VE_USB: not modelled */ - /* ??? Hack to map an additional page of ram for the secondary CPU - startup code. I guess this works on real hardware because the - BootROM happens to be in ROM/flash or in memory that isn't clobbered - until after Linux boots the secondary CPUs. */ - memory_region_init_ram(hackram, "vexpress.hack", 0x1000); - vmstate_register_ram_global(hackram); - memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, hackram); + /* VE_DAPROM: not modelled */ vexpress_binfo.ram_size = ram_size; vexpress_binfo.kernel_filename = kernel_filename; @@ -221,10 +434,36 @@ static void vexpress_a9_init(ram_addr_t ram_size, vexpress_binfo.initrd_filename = initrd_filename; vexpress_binfo.nb_cpus = smp_cpus; vexpress_binfo.board_id = VEXPRESS_BOARD_ID; - vexpress_binfo.loader_start = 0x60000000; + vexpress_binfo.loader_start = daughterboard->loader_start; + vexpress_binfo.smp_loader_start = map[VE_SRAM]; + vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30; + vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr; arm_load_kernel(first_cpu, &vexpress_binfo); } +static void vexpress_a9_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + vexpress_common_init(&a9_daughterboard, + ram_size, boot_device, kernel_filename, + kernel_cmdline, initrd_filename, cpu_model); +} + +static void vexpress_a15_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + vexpress_common_init(&a15_daughterboard, + ram_size, boot_device, kernel_filename, + kernel_cmdline, initrd_filename, cpu_model); +} static QEMUMachine vexpress_a9_machine = { .name = "vexpress-a9", @@ -234,9 +473,18 @@ static QEMUMachine vexpress_a9_machine = { .max_cpus = 4, }; +static QEMUMachine vexpress_a15_machine = { + .name = "vexpress-a15", + .desc = "ARM Versatile Express for Cortex-A15", + .init = vexpress_a15_init, + .use_scsi = 1, + .max_cpus = 4, +}; + static void vexpress_machine_init(void) { qemu_register_machine(&vexpress_a9_machine); + qemu_register_machine(&vexpress_a15_machine); } machine_init(vexpress_machine_init); diff --git a/hw/vga-isa.c b/hw/vga-isa.c index 8d3ff0d47b..4bcc4db62f 100644 --- a/hw/vga-isa.c +++ b/hw/vga-isa.c @@ -85,8 +85,9 @@ static TypeInfo vga_info = { .class_init = vga_class_initfn, }; -static void vga_register(void) +static void vga_register_types(void) { type_register_static(&vga_info); } -device_init(vga_register) + +type_init(vga_register_types) diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 974a7a9d6f..465b643d21 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -96,8 +96,9 @@ static TypeInfo vga_info = { .class_init = vga_class_init, }; -static void vga_register(void) +static void vga_register_types(void) { type_register_static(&vga_info); } -device_init(vga_register); + +type_init(vga_register_types) @@ -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 a5a439668b..49990f8efe 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-console.c b/hw/virtio-console.c index 4f2c3e4379..cffee3d470 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -149,12 +149,6 @@ static TypeInfo virtconsole_info = { .class_init = virtconsole_class_init, }; -static void virtconsole_register(void) -{ - type_register_static(&virtconsole_info); -} -device_init(virtconsole_register) - static Property virtserialport_properties[] = { DEFINE_PROP_CHR("chardev", VirtConsole, chr), DEFINE_PROP_END_OF_LIST(), @@ -179,8 +173,10 @@ static TypeInfo virtserialport_info = { .class_init = virtserialport_class_init, }; -static void virtserialport_register(void) +static void virtconsole_register_types(void) { + type_register_static(&virtconsole_info); type_register_static(&virtserialport_info); } -device_init(virtserialport_register) + +type_init(virtconsole_register_types) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 93fff54782..a0fb7c1b9c 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 void virtio_pci_register_devices(void) +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); } -device_init(virtio_pci_register_devices) +type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 344c22b68f..e5604282e5 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 0000000000..e607edc915 --- /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 0000000000..4bc889de02 --- /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-serial-bus.c b/hw/virtio-serial-bus.c index 61176299a3..e22940ecb2 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -949,9 +949,9 @@ static TypeInfo virtio_serial_port_type_info = { .class_init = virtio_serial_port_class_init, }; -static void virtio_serial_register_devices(void) +static void virtio_serial_register_types(void) { type_register_static(&virtio_serial_port_type_info); } -device_init(virtio_serial_register_devices); +type_init(virtio_serial_register_types) diff --git a/hw/virtio.c b/hw/virtio.c index 74cc038af9..064aecf553 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -845,8 +845,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) void virtio_cleanup(VirtIODevice *vdev) { qemu_del_vm_change_state_handler(vdev->vmstate); - if (vdev->config) - g_free(vdev->config); + g_free(vdev->config); g_free(vdev->vq); g_free(vdev); } diff --git a/hw/virtio.h b/hw/virtio.h index 25f55647b4..400c092c95 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/vmmouse.c b/hw/vmmouse.c index fda4f89a76..6338efa1c3 100644 --- a/hw/vmmouse.c +++ b/hw/vmmouse.c @@ -294,8 +294,9 @@ static TypeInfo vmmouse_info = { .class_init = vmmouse_class_initfn, }; -static void vmmouse_dev_register(void) +static void vmmouse_register_types(void) { type_register_static(&vmmouse_info); } -device_init(vmmouse_dev_register) + +type_init(vmmouse_register_types) diff --git a/hw/vmport.c b/hw/vmport.c index a2c45e1950..9373be9775 100644 --- a/hw/vmport.c +++ b/hw/vmport.c @@ -159,8 +159,9 @@ static TypeInfo vmport_info = { .class_init = vmport_class_initfn, }; -static void vmport_dev_register(void) +static void vmport_register_types(void) { type_register_static(&vmport_info); } -device_init(vmport_dev_register) + +type_init(vmport_register_types) diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index 3f3eb21d02..142d9f4ea0 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; } @@ -1223,8 +1223,9 @@ static TypeInfo vmsvga_info = { .class_init = vmsvga_class_init, }; -static void vmsvga_register(void) +static void vmsvga_register_types(void) { type_register_static(&vmsvga_info); } -device_init(vmsvga_register); + +type_init(vmsvga_register_types) diff --git a/hw/vt82c686.c b/hw/vt82c686.c index aa0954f487..6fb7950fa6 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() } }; @@ -366,13 +364,6 @@ static TypeInfo via_ac97_info = { .class_init = via_ac97_class_init, }; -static void vt82c686b_ac97_register(void) -{ - type_register_static(&via_ac97_info); -} - -device_init(vt82c686b_ac97_register); - static int vt82c686b_mc97_initfn(PCIDevice *dev) { VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev); @@ -414,13 +405,6 @@ static TypeInfo via_mc97_info = { .class_init = via_mc97_class_init, }; -static void vt82c686b_mc97_register(void) -{ - type_register_static(&via_mc97_info); -} - -device_init(vt82c686b_mc97_register); - /* vt82c686 pm init */ static int vt82c686b_pm_initfn(PCIDevice *dev) { @@ -445,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); @@ -497,13 +481,6 @@ static TypeInfo via_pm_info = { .class_init = via_pm_class_init, }; -static void vt82c686b_pm_register(void) -{ - type_register_static(&via_pm_info); -} - -device_init(vt82c686b_pm_register); - static const VMStateDescription vmstate_via = { .name = "vt82c686b", .version_id = 1, @@ -571,8 +548,12 @@ static TypeInfo via_info = { .class_init = via_class_init, }; -static void vt82c686b_register(void) +static void vt82c686b_register_types(void) { + type_register_static(&via_ac97_info); + type_register_static(&via_mc97_info); + type_register_static(&via_pm_info); type_register_static(&via_info); } -device_init(vt82c686b_register); + +type_init(vt82c686b_register_types) diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c index 41325f0955..15c69db932 100644 --- a/hw/wdt_i6300esb.c +++ b/hw/wdt_i6300esb.c @@ -448,10 +448,10 @@ static TypeInfo i6300esb_info = { .class_init = i6300esb_class_init, }; -static void i6300esb_register_devices(void) +static void i6300esb_register_types(void) { watchdog_add_model(&model); type_register_static(&i6300esb_info); } -device_init(i6300esb_register_devices); +type_init(i6300esb_register_types) diff --git a/hw/wdt_ib700.c b/hw/wdt_ib700.c index 8faa2316c9..7f6c21d809 100644 --- a/hw/wdt_ib700.c +++ b/hw/wdt_ib700.c @@ -136,10 +136,10 @@ static TypeInfo wdt_ib700_info = { .class_init = wdt_ib700_class_init, }; -static void wdt_ib700_register_devices(void) +static void wdt_ib700_register_types(void) { watchdog_add_model(&model); type_register_static(&wdt_ib700_info); } -device_init(wdt_ib700_register_devices); +type_init(wdt_ib700_register_types) diff --git a/hw/wm8750.c b/hw/wm8750.c index 18afa4c1ac..11bcec3410 100644 --- a/hw/wm8750.c +++ b/hw/wm8750.c @@ -708,9 +708,9 @@ static TypeInfo wm8750_info = { .class_init = wm8750_class_init, }; -static void wm8750_register_devices(void) +static void wm8750_register_types(void) { type_register_static(&wm8750_info); } -device_init(wm8750_register_devices) +type_init(wm8750_register_types) diff --git a/hw/xen_platform.c b/hw/xen_platform.c index e7571022e4..5a7c4cc97b 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -396,9 +396,9 @@ static TypeInfo xen_platform_info = { .class_init = xen_platform_class_init, }; -static void xen_platform_register(void) +static void xen_platform_register_types(void) { type_register_static(&xen_platform_info); } -device_init(xen_platform_register); +type_init(xen_platform_register_types) diff --git a/hw/xgmac.c b/hw/xgmac.c index d395b1cc2d..dd4bdc46f5 100644 --- a/hw/xgmac.c +++ b/hw/xgmac.c @@ -425,9 +425,9 @@ static TypeInfo xgmac_enet_info = { .class_init = xgmac_enet_class_init, }; -static void xgmac_enet_register(void) +static void xgmac_enet_register_types(void) { type_register_static(&xgmac_enet_info); } -device_init(xgmac_enet_register) +type_init(xgmac_enet_register_types) diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index e8a53123f9..85dfcbf2b9 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -508,9 +508,9 @@ static TypeInfo axidma_info = { .class_init = axidma_class_init, }; -static void xilinx_axidma_register(void) +static void xilinx_axidma_register_types(void) { type_register_static(&axidma_info); } -device_init(xilinx_axidma_register) +type_init(xilinx_axidma_register_types) diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 1ce2db4510..7526273b4d 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -894,9 +894,10 @@ static TypeInfo xilinx_enet_info = { .instance_size = sizeof(struct XilinxAXIEnet), .class_init = xilinx_enet_class_init, }; -static void xilinx_enet_register(void) + +static void xilinx_enet_register_types(void) { type_register_static(&xilinx_enet_info); } -device_init(xilinx_enet_register) +type_init(xilinx_enet_register_types) diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c index 499feef488..857b33d172 100644 --- a/hw/xilinx_ethlite.c +++ b/hw/xilinx_ethlite.c @@ -249,9 +249,9 @@ static TypeInfo xilinx_ethlite_info = { .class_init = xilinx_ethlite_class_init, }; -static void xilinx_ethlite_register(void) +static void xilinx_ethlite_register_types(void) { type_register_static(&xilinx_ethlite_info); } -device_init(xilinx_ethlite_register) +type_init(xilinx_ethlite_register_types) diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c index 73eed6dc5f..553f8488f6 100644 --- a/hw/xilinx_intc.c +++ b/hw/xilinx_intc.c @@ -182,9 +182,9 @@ static TypeInfo xilinx_intc_info = { .class_init = xilinx_intc_class_init, }; -static void xilinx_intc_register(void) +static void xilinx_intc_register_types(void) { type_register_static(&xilinx_intc_info); } -device_init(xilinx_intc_register) +type_init(xilinx_intc_register_types) diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c index c8236d2e2a..3ab2f2bb03 100644 --- a/hw/xilinx_timer.c +++ b/hw/xilinx_timer.c @@ -241,9 +241,9 @@ static TypeInfo xilinx_timer_info = { .class_init = xilinx_timer_class_init, }; -static void xilinx_timer_register(void) +static void xilinx_timer_register_types(void) { type_register_static(&xilinx_timer_info); } -device_init(xilinx_timer_register) +type_init(xilinx_timer_register_types) diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c index 1c2b9087b4..aa0170db49 100644 --- a/hw/xilinx_uartlite.c +++ b/hw/xilinx_uartlite.c @@ -225,9 +225,9 @@ static TypeInfo xilinx_uartlite_info = { .class_init = xilinx_uartlite_class_init, }; -static void xilinx_uart_register(void) +static void xilinx_uart_register_types(void) { type_register_static(&xilinx_uartlite_info); } -device_init(xilinx_uart_register) +type_init(xilinx_uart_register_types) diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c index 07e4fc1018..319624f212 100644 --- a/hw/xio3130_downstream.c +++ b/hw/xio3130_downstream.c @@ -203,12 +203,12 @@ static TypeInfo xio3130_downstream_info = { .class_init = xio3130_downstream_class_init, }; -static void xio3130_downstream_register(void) +static void xio3130_downstream_register_types(void) { type_register_static(&xio3130_downstream_info); } -device_init(xio3130_downstream_register); +type_init(xio3130_downstream_register_types) /* * Local variables: diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c index 7887c92fcc..34a99bba08 100644 --- a/hw/xio3130_upstream.c +++ b/hw/xio3130_upstream.c @@ -177,12 +177,12 @@ static TypeInfo xio3130_upstream_info = { .class_init = xio3130_upstream_class_init, }; -static void xio3130_upstream_register(void) +static void xio3130_upstream_register_types(void) { type_register_static(&xio3130_upstream_info); } -device_init(xio3130_upstream_register); +type_init(xio3130_upstream_register_types) /* diff --git a/hw/zaurus.c b/hw/zaurus.c index 055df9b4e2..72838ec440 100644 --- a/hw/zaurus.c +++ b/hw/zaurus.c @@ -243,11 +243,12 @@ static TypeInfo scoop_sysbus_info = { .class_init = scoop_sysbus_class_init, }; -static void scoop_register(void) +static void scoop_register_types(void) { type_register_static(&scoop_sysbus_info); } -device_init(scoop_register); + +type_init(scoop_register_types) /* Write the bootloader parameters memory area. */ |