From 09a573474ba0bb83df8159d0f24c0f209734da33 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 9 Feb 2018 18:51:40 +0000 Subject: cuda: convert to use the shared mos6522 device Add the relevant hooks as required for the MacOS timer calibration and delayed SR interrupt. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 606 ++++++++++++++------------------------------------- hw/ppc/mac.h | 87 ++++---- 2 files changed, 204 insertions(+), 489 deletions(-) (limited to 'hw') diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index a185252..54c02ae 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -26,15 +26,14 @@ #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/input/adb.h" +#include "hw/misc/mos6522.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "qemu/log.h" -/* XXX: implement all timer modes */ - -/* debug CUDA */ -//#define DEBUG_CUDA +/* debug CUDA packets */ +//#define DEBUG_CUDA_PACKET /* debug CUDA packets */ //#define DEBUG_CUDA_PACKET @@ -47,426 +46,114 @@ #endif /* Bits in B data register: all active low */ -#define TREQ 0x08 /* Transfer request (input) */ -#define TACK 0x10 /* Transfer acknowledge (output) */ -#define TIP 0x20 /* Transfer in progress (output) */ - -/* Bits in ACR */ -#define SR_CTRL 0x1c /* Shift register control bits */ -#define SR_EXT 0x0c /* Shift on external clock */ -#define SR_OUT 0x10 /* Shift out if 1 */ - -/* Bits in IFR and IER */ -#define IER_SET 0x80 /* set bits in IER */ -#define IER_CLR 0 /* clear bits in IER */ -#define SR_INT 0x04 /* Shift register full/empty */ -#define SR_DATA_INT 0x08 -#define SR_CLOCK_INT 0x10 -#define T1_INT 0x40 /* Timer 1 interrupt */ -#define T2_INT 0x20 /* Timer 2 interrupt */ - -/* Bits in ACR */ -#define T1MODE 0xc0 /* Timer 1 mode */ -#define T1MODE_CONT 0x40 /* continuous interrupts */ +#define TREQ 0x08 /* Transfer request (input) */ +#define TACK 0x10 /* Transfer acknowledge (output) */ +#define TIP 0x20 /* Transfer in progress (output) */ /* commands (1st byte) */ -#define ADB_PACKET 0 -#define CUDA_PACKET 1 -#define ERROR_PACKET 2 -#define TIMER_PACKET 3 -#define POWER_PACKET 4 -#define MACIIC_PACKET 5 -#define PMU_PACKET 6 - - -/* CUDA commands (2nd byte) */ -#define CUDA_WARM_START 0x0 -#define CUDA_AUTOPOLL 0x1 -#define CUDA_GET_6805_ADDR 0x2 -#define CUDA_GET_TIME 0x3 -#define CUDA_GET_PRAM 0x7 -#define CUDA_SET_6805_ADDR 0x8 -#define CUDA_SET_TIME 0x9 -#define CUDA_POWERDOWN 0xa -#define CUDA_POWERUP_TIME 0xb -#define CUDA_SET_PRAM 0xc -#define CUDA_MS_RESET 0xd -#define CUDA_SEND_DFAC 0xe -#define CUDA_BATTERY_SWAP_SENSE 0x10 -#define CUDA_RESET_SYSTEM 0x11 -#define CUDA_SET_IPL 0x12 -#define CUDA_FILE_SERVER_FLAG 0x13 -#define CUDA_SET_AUTO_RATE 0x14 -#define CUDA_GET_AUTO_RATE 0x16 -#define CUDA_SET_DEVICE_LIST 0x19 -#define CUDA_GET_DEVICE_LIST 0x1a -#define CUDA_SET_ONE_SECOND_MODE 0x1b -#define CUDA_SET_POWER_MESSAGES 0x21 -#define CUDA_GET_SET_IIC 0x22 -#define CUDA_WAKEUP 0x23 -#define CUDA_TIMER_TICKLE 0x24 -#define CUDA_COMBINED_FORMAT_IIC 0x25 +#define ADB_PACKET 0 +#define CUDA_PACKET 1 +#define ERROR_PACKET 2 +#define TIMER_PACKET 3 +#define POWER_PACKET 4 +#define MACIIC_PACKET 5 +#define PMU_PACKET 6 #define CUDA_TIMER_FREQ (4700000 / 6) /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 -/* CUDA registers */ -#define CUDA_REG_B 0x00 -#define CUDA_REG_A 0x01 -#define CUDA_REG_DIRB 0x02 -#define CUDA_REG_DIRA 0x03 -#define CUDA_REG_T1CL 0x04 -#define CUDA_REG_T1CH 0x05 -#define CUDA_REG_T1LL 0x06 -#define CUDA_REG_T1LH 0x07 -#define CUDA_REG_T2CL 0x08 -#define CUDA_REG_T2CH 0x09 -#define CUDA_REG_SR 0x0a -#define CUDA_REG_ACR 0x0b -#define CUDA_REG_PCR 0x0c -#define CUDA_REG_IFR 0x0d -#define CUDA_REG_IER 0x0e -#define CUDA_REG_ANH 0x0f - -static void cuda_update(CUDAState *s); static void cuda_receive_packet_from_host(CUDAState *s, const uint8_t *data, int len); -static void cuda_timer_update(CUDAState *s, CUDATimer *ti, - int64_t current_time); -static void cuda_update_irq(CUDAState *s) -{ - if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} +/* MacOS uses timer 1 for calibration on startup, so we use + * the timebase frequency and cuda_get_counter_value() with + * cuda_get_load_time() to steer MacOS to calculate calibrate its timers + * correctly for both TCG and KVM (see commit b981289c49 "PPC: Cuda: Use cuda + * timer to expose tbfreq to guest" for more information) */ -static uint64_t get_counter_value(CUDAState *s, CUDATimer *ti) +static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { + MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); + CUDAState *cs = mcs->cuda; + /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */ uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - s->tb_frequency, NANOSECONDS_PER_SECOND) - + cs->tb_frequency, NANOSECONDS_PER_SECOND) - ti->load_time; - return (tb_diff * 0xBF401675E5DULL) / (s->tb_frequency << 24); + return (tb_diff * 0xBF401675E5DULL) / (cs->tb_frequency << 24); } -static uint64_t get_counter_load_time(CUDAState *s, CUDATimer *ti) +static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) { + MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); + CUDAState *cs = mcs->cuda; + uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - s->tb_frequency, NANOSECONDS_PER_SECOND); + cs->tb_frequency, NANOSECONDS_PER_SECOND); return load_time; } -static unsigned int get_counter(CUDAState *s, CUDATimer *ti) -{ - int64_t d; - unsigned int counter; - - d = get_counter_value(s, ti); - - if (ti->index == 0) { - /* the timer goes down from latch to -1 (period of latch + 2) */ - if (d <= (ti->counter_value + 1)) { - counter = (ti->counter_value - d) & 0xffff; - } else { - counter = (d - (ti->counter_value + 1)) % (ti->latch + 2); - counter = (ti->latch - counter) & 0xffff; - } - } else { - counter = (ti->counter_value - d) & 0xffff; - } - return counter; -} - -static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) -{ - CUDA_DPRINTF("T%d.counter=%d\n", 1 + ti->index, val); - ti->load_time = get_counter_load_time(s, ti); - ti->counter_value = val; - cuda_timer_update(s, ti, ti->load_time); -} - -static int64_t get_next_irq_time(CUDATimer *ti, int64_t current_time) -{ - int64_t d, next_time; - unsigned int counter; - - /* current counter value */ - d = muldiv64(current_time - ti->load_time, - ti->frequency, NANOSECONDS_PER_SECOND); - /* the timer goes down from latch to -1 (period of latch + 2) */ - if (d <= (ti->counter_value + 1)) { - counter = (ti->counter_value - d) & 0xffff; - } else { - counter = (d - (ti->counter_value + 1)) % (ti->latch + 2); - counter = (ti->latch - counter) & 0xffff; - } - - /* Note: we consider the irq is raised on 0 */ - if (counter == 0xffff) { - next_time = d + ti->latch + 1; - } else if (counter == 0) { - next_time = d + ti->latch + 2; - } else { - next_time = d + counter; - } - CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n", - ti->latch, d, next_time - d); - next_time = muldiv64(next_time, NANOSECONDS_PER_SECOND, ti->frequency) + - ti->load_time; - if (next_time <= current_time) { - next_time = current_time + 1; - } - return next_time; -} - -static void cuda_timer_update(CUDAState *s, CUDATimer *ti, - int64_t current_time) -{ - if (!ti->timer) - return; - if (ti->index == 0 && (s->acr & T1MODE) != T1MODE_CONT) { - timer_del(ti->timer); - } else { - ti->next_irq_time = get_next_irq_time(ti, current_time); - timer_mod(ti->timer, ti->next_irq_time); - } -} - -static void cuda_timer1(void *opaque) -{ - CUDAState *s = opaque; - CUDATimer *ti = &s->timers[0]; - - cuda_timer_update(s, ti, ti->next_irq_time); - s->ifr |= T1_INT; - cuda_update_irq(s); -} - -static void cuda_timer2(void *opaque) -{ - CUDAState *s = opaque; - CUDATimer *ti = &s->timers[1]; - - cuda_timer_update(s, ti, ti->next_irq_time); - s->ifr |= T2_INT; - cuda_update_irq(s); -} - static void cuda_set_sr_int(void *opaque) { CUDAState *s = opaque; + MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522State *ms = MOS6522(mcs); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); - CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); - s->ifr |= SR_INT; - cuda_update_irq(s); + mdc->set_sr_int(ms); } static void cuda_delay_set_sr_int(CUDAState *s) { + MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522State *ms = MOS6522(mcs); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); int64_t expire; - if (s->dirb == 0xff) { - /* Not in Mac OS, fire the IRQ directly */ - cuda_set_sr_int(s); + if (ms->dirb == 0xff || s->sr_delay_ns == 0) { + /* Disabled or not in Mac OS, fire the IRQ directly */ + mdc->set_sr_int(ms); return; } CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); - expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 300 * SCALE_US; + expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns; timer_mod(s->sr_delay_timer, expire); } -static uint64_t cuda_read(void *opaque, hwaddr addr, unsigned size) -{ - CUDAState *s = opaque; - uint32_t val; - - addr = (addr >> 9) & 0xf; - switch(addr) { - case CUDA_REG_B: - val = s->b; - break; - case CUDA_REG_A: - val = s->a; - break; - case CUDA_REG_DIRB: - val = s->dirb; - break; - case CUDA_REG_DIRA: - val = s->dira; - break; - case CUDA_REG_T1CL: - val = get_counter(s, &s->timers[0]) & 0xff; - s->ifr &= ~T1_INT; - cuda_update_irq(s); - break; - case CUDA_REG_T1CH: - val = get_counter(s, &s->timers[0]) >> 8; - cuda_update_irq(s); - break; - case CUDA_REG_T1LL: - val = s->timers[0].latch & 0xff; - break; - case CUDA_REG_T1LH: - /* XXX: check this */ - val = (s->timers[0].latch >> 8) & 0xff; - break; - case CUDA_REG_T2CL: - val = get_counter(s, &s->timers[1]) & 0xff; - s->ifr &= ~T2_INT; - cuda_update_irq(s); - break; - case CUDA_REG_T2CH: - val = get_counter(s, &s->timers[1]) >> 8; - break; - case CUDA_REG_SR: - val = s->sr; - s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT); - cuda_update_irq(s); - break; - case CUDA_REG_ACR: - val = s->acr; - break; - case CUDA_REG_PCR: - val = s->pcr; - break; - case CUDA_REG_IFR: - val = s->ifr; - if (s->ifr & s->ier) { - val |= 0x80; - } - break; - case CUDA_REG_IER: - val = s->ier | 0x80; - break; - default: - case CUDA_REG_ANH: - val = s->anh; - break; - } - if (addr != CUDA_REG_IFR || val != 0) { - CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val); - } - - return val; -} - -static void cuda_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) -{ - CUDAState *s = opaque; - - addr = (addr >> 9) & 0xf; - CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val); - - switch(addr) { - case CUDA_REG_B: - s->b = (s->b & ~s->dirb) | (val & s->dirb); - cuda_update(s); - break; - case CUDA_REG_A: - s->a = (s->a & ~s->dira) | (val & s->dira); - break; - case CUDA_REG_DIRB: - s->dirb = val; - break; - case CUDA_REG_DIRA: - s->dira = val; - break; - case CUDA_REG_T1CL: - s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; - cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - break; - case CUDA_REG_T1CH: - s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); - s->ifr &= ~T1_INT; - set_counter(s, &s->timers[0], s->timers[0].latch); - break; - case CUDA_REG_T1LL: - s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; - cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - break; - case CUDA_REG_T1LH: - s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); - s->ifr &= ~T1_INT; - cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - break; - case CUDA_REG_T2CL: - s->timers[1].latch = (s->timers[1].latch & 0xff00) | val; - break; - case CUDA_REG_T2CH: - /* To ensure T2 generates an interrupt on zero crossing with the - common timer code, write the value directly from the latch to - the counter */ - s->timers[1].latch = (s->timers[1].latch & 0xff) | (val << 8); - s->ifr &= ~T2_INT; - set_counter(s, &s->timers[1], s->timers[1].latch); - break; - case CUDA_REG_SR: - s->sr = val; - break; - case CUDA_REG_ACR: - s->acr = val; - cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - break; - case CUDA_REG_PCR: - s->pcr = val; - break; - case CUDA_REG_IFR: - /* reset bits */ - s->ifr &= ~val; - cuda_update_irq(s); - break; - case CUDA_REG_IER: - if (val & IER_SET) { - /* set bits */ - s->ier |= val & 0x7f; - } else { - /* reset bits */ - s->ier &= ~val; - } - cuda_update_irq(s); - break; - default: - case CUDA_REG_ANH: - s->anh = val; - break; - } -} - /* NOTE: TIP and TREQ are negated */ static void cuda_update(CUDAState *s) { + MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522State *ms = MOS6522(mcs); int packet_received, len; packet_received = 0; - if (!(s->b & TIP)) { + if (!(ms->b & TIP)) { /* transfer requested from host */ - if (s->acr & SR_OUT) { + if (ms->acr & SR_OUT) { /* data output */ - if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { + if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { if (s->data_out_index < sizeof(s->data_out)) { - CUDA_DPRINTF("send: %02x\n", s->sr); - s->data_out[s->data_out_index++] = s->sr; + CUDA_DPRINTF("send: %02x\n", ms->sr); + s->data_out[s->data_out_index++] = ms->sr; cuda_delay_set_sr_int(s); } } } else { if (s->data_in_index < s->data_in_size) { /* data input */ - if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { - s->sr = s->data_in[s->data_in_index++]; - CUDA_DPRINTF("recv: %02x\n", s->sr); + if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { + ms->sr = s->data_in[s->data_in_index++]; + CUDA_DPRINTF("recv: %02x\n", ms->sr); /* indicate end of transfer */ if (s->data_in_index >= s->data_in_size) { - s->b = (s->b | TREQ); + ms->b = (ms->b | TREQ); } cuda_delay_set_sr_int(s); } @@ -474,12 +161,13 @@ static void cuda_update(CUDAState *s) } } else { /* no transfer requested: handle sync case */ - if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) { + if ((s->last_b & TIP) && (ms->b & TACK) != (s->last_b & TACK)) { /* update TREQ state each time TACK change state */ - if (s->b & TACK) - s->b = (s->b | TREQ); - else - s->b = (s->b & ~TREQ); + if (ms->b & TACK) { + ms->b = (ms->b | TREQ); + } else { + ms->b = (ms->b & ~TREQ); + } cuda_delay_set_sr_int(s); } else { if (!(s->last_b & TIP)) { @@ -490,13 +178,13 @@ static void cuda_update(CUDAState *s) } /* signal if there is data to read */ if (s->data_in_index < s->data_in_size) { - s->b = (s->b & ~TREQ); + ms->b = (ms->b & ~TREQ); } } } - s->last_acr = s->acr; - s->last_b = s->b; + s->last_acr = ms->acr; + s->last_b = ms->b; /* NOTE: cuda_receive_packet_from_host() can call cuda_update() recursively */ @@ -538,9 +226,8 @@ static void cuda_adb_poll(void *opaque) obuf[1] = 0x40; /* polled data */ cuda_send_packet_to_host(s, obuf, olen + 2); } - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); + timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); } /* description of commands */ @@ -787,35 +474,35 @@ static void cuda_receive_packet_from_host(CUDAState *s, } } -static const MemoryRegionOps cuda_ops = { - .read = cuda_read, - .write = cuda_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; +static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size) +{ + CUDAState *s = opaque; + MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522State *ms = MOS6522(mcs); -static bool cuda_timer_exist(void *opaque, int version_id) + addr = (addr >> 9) & 0xf; + return mos6522_read(ms, addr, size); +} + +static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { - CUDATimer *s = opaque; + CUDAState *s = opaque; + MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522State *ms = MOS6522(mcs); - return s->timer != NULL; + addr = (addr >> 9) & 0xf; + mos6522_write(ms, addr, val, size); } -static const VMStateDescription vmstate_cuda_timer = { - .name = "cuda_timer", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT16(latch, CUDATimer), - VMSTATE_UINT16(counter_value, CUDATimer), - VMSTATE_INT64(load_time, CUDATimer), - VMSTATE_INT64(next_irq_time, CUDATimer), - VMSTATE_TIMER_PTR_TEST(timer, CUDATimer, cuda_timer_exist), - VMSTATE_END_OF_LIST() - } +static const MemoryRegionOps mos6522_cuda_ops = { + .read = mos6522_cuda_read, + .write = mos6522_cuda_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, }; static const VMStateDescription vmstate_cuda = { @@ -823,18 +510,8 @@ static const VMStateDescription vmstate_cuda = { .version_id = 4, .minimum_version_id = 4, .fields = (VMStateField[]) { - VMSTATE_UINT8(a, CUDAState), - VMSTATE_UINT8(b, CUDAState), VMSTATE_UINT8(last_b, CUDAState), - VMSTATE_UINT8(dira, CUDAState), - VMSTATE_UINT8(dirb, CUDAState), - VMSTATE_UINT8(sr, CUDAState), - VMSTATE_UINT8(acr, CUDAState), VMSTATE_UINT8(last_acr, CUDAState), - VMSTATE_UINT8(pcr, CUDAState), - VMSTATE_UINT8(ifr, CUDAState), - VMSTATE_UINT8(ier, CUDAState), - VMSTATE_UINT8(anh, CUDAState), VMSTATE_INT32(data_in_size, CUDAState), VMSTATE_INT32(data_in_index, CUDAState), VMSTATE_INT32(data_out_index, CUDAState), @@ -844,8 +521,6 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_BUFFER(data_in, CUDAState), VMSTATE_BUFFER(data_out, CUDAState), VMSTATE_UINT32(tick_offset, CUDAState), - VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, - vmstate_cuda_timer, CUDATimer), VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState), VMSTATE_END_OF_LIST() @@ -856,61 +531,48 @@ static void cuda_reset(DeviceState *dev) { CUDAState *s = CUDA(dev); - s->b = 0; - s->a = 0; - s->dirb = 0xff; - s->dira = 0; - s->sr = 0; - s->acr = 0; - s->pcr = 0; - s->ifr = 0; - s->ier = 0; - // s->ier = T1_INT | SR_INT; - s->anh = 0; s->data_in_size = 0; s->data_in_index = 0; s->data_out_index = 0; s->autopoll = 0; - - s->timers[0].latch = 0xffff; - set_counter(s, &s->timers[0], 0xffff); - - s->timers[1].latch = 0xffff; - - s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); } -static void cuda_realizefn(DeviceState *dev, Error **errp) +static void cuda_realize(DeviceState *dev, Error **errp) { CUDAState *s = CUDA(dev); + SysBusDevice *sbd; + MOS6522State *ms; + DeviceState *d; struct tm tm; - s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s); - s->timers[0].frequency = CUDA_TIMER_FREQ; - s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer2, s); - s->timers[1].frequency = (SCALE_US * 6000) / 4700; + d = qdev_create(NULL, TYPE_MOS6522_CUDA); + object_property_set_link(OBJECT(d), OBJECT(s), "cuda", errp); + qdev_init_nofail(d); + s->mos6522_cuda = MOS6522_CUDA(d); + + /* Pass IRQ from 6522 */ + ms = MOS6522(d); + sbd = SYS_BUS_DEVICE(s); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; + s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); + s->sr_delay_ns = 300 * SCALE_US; + s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); - s->autopoll_rate_ms = 20; s->adb_poll_mask = 0xffff; + s->autopoll_rate_ms = 20; } -static void cuda_initfn(Object *obj) +static void cuda_init(Object *obj) { - SysBusDevice *d = SYS_BUS_DEVICE(obj); CUDAState *s = CUDA(obj); - int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000); - sysbus_init_mmio(d, &s->mem); - sysbus_init_irq(d, &s->irq); - - for (i = 0; i < ARRAY_SIZE(s->timers); i++) { - s->timers[i].index = i; - } + memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); + sysbus_init_mmio(sbd, &s->mem); qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, DEVICE(obj), "adb.0"); @@ -925,7 +587,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->realize = cuda_realizefn; + dc->realize = cuda_realize; dc->reset = cuda_reset; dc->vmsd = &vmstate_cuda; dc->props = cuda_properties; @@ -936,12 +598,62 @@ static const TypeInfo cuda_type_info = { .name = TYPE_CUDA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(CUDAState), - .instance_init = cuda_initfn, + .instance_init = cuda_init, .class_init = cuda_class_init, }; +static void mos6522_cuda_portB_write(MOS6522State *s) +{ + MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); + + cuda_update(mcs->cuda); +} + +static void mos6522_cuda_realize(DeviceState *dev, Error **errp) +{ + MOS6522State *ms = MOS6522(dev); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->parent_realize(dev, errp); + + ms->timers[0].frequency = CUDA_TIMER_FREQ; + ms->timers[1].frequency = (SCALE_US * 6000) / 4700; +} + +static void mos6522_cuda_init(Object *obj) +{ + MOS6522CUDAState *s = MOS6522_CUDA(obj); + + object_property_add_link(obj, "cuda", TYPE_CUDA, + (Object **) &s->cuda, + qdev_prop_allow_set_link_before_realize, + 0, NULL); +} + +static void mos6522_cuda_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); + + dc->realize = mos6522_cuda_realize; + mdc->portB_write = mos6522_cuda_portB_write; + mdc->get_timer1_counter_value = cuda_get_counter_value; + mdc->get_timer2_counter_value = cuda_get_counter_value; + mdc->get_timer1_load_time = cuda_get_load_time; + mdc->get_timer2_load_time = cuda_get_load_time; +} + +static const TypeInfo mos6522_cuda_type_info = { + .name = TYPE_MOS6522_CUDA, + .parent = TYPE_MOS6522, + .instance_size = sizeof(MOS6522CUDAState), + .instance_init = mos6522_cuda_init, + .class_init = mos6522_cuda_class_init, +}; + static void cuda_register_types(void) { + type_register_static(&mos6522_cuda_type_info); type_register_static(&cuda_type_info); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index fa78115..3e9f13d 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -30,6 +30,7 @@ #include "hw/sysbus.h" #include "hw/ide/internal.h" #include "hw/input/adb.h" +#include "hw/misc/mos6522.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -44,59 +45,48 @@ #define ESCC_CLOCK 3686400 +/* CUDA commands (2nd byte) */ +#define CUDA_WARM_START 0x0 +#define CUDA_AUTOPOLL 0x1 +#define CUDA_GET_6805_ADDR 0x2 +#define CUDA_GET_TIME 0x3 +#define CUDA_GET_PRAM 0x7 +#define CUDA_SET_6805_ADDR 0x8 +#define CUDA_SET_TIME 0x9 +#define CUDA_POWERDOWN 0xa +#define CUDA_POWERUP_TIME 0xb +#define CUDA_SET_PRAM 0xc +#define CUDA_MS_RESET 0xd +#define CUDA_SEND_DFAC 0xe +#define CUDA_BATTERY_SWAP_SENSE 0x10 +#define CUDA_RESET_SYSTEM 0x11 +#define CUDA_SET_IPL 0x12 +#define CUDA_FILE_SERVER_FLAG 0x13 +#define CUDA_SET_AUTO_RATE 0x14 +#define CUDA_GET_AUTO_RATE 0x16 +#define CUDA_SET_DEVICE_LIST 0x19 +#define CUDA_GET_DEVICE_LIST 0x1a +#define CUDA_SET_ONE_SECOND_MODE 0x1b +#define CUDA_SET_POWER_MESSAGES 0x21 +#define CUDA_GET_SET_IIC 0x22 +#define CUDA_WAKEUP 0x23 +#define CUDA_TIMER_TICKLE 0x24 +#define CUDA_COMBINED_FORMAT_IIC 0x25 + /* Cuda */ #define TYPE_CUDA "cuda" #define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) -/** - * CUDATimer: - * @counter_value: counter value at load time - */ -typedef struct CUDATimer { - int index; - uint16_t latch; - uint16_t counter_value; - int64_t load_time; - int64_t next_irq_time; - uint64_t frequency; - QEMUTimer *timer; -} CUDATimer; - -/** - * CUDAState: - * @b: B-side data - * @a: A-side data - * @dirb: B-side direction (1=output) - * @dira: A-side direction (1=output) - * @sr: Shift register - * @acr: Auxiliary control register - * @pcr: Peripheral control register - * @ifr: Interrupt flag register - * @ier: Interrupt enable register - * @anh: A-side data, no handshake - * @last_b: last value of B register - * @last_acr: last value of ACR register - */ +typedef struct MOS6522CUDAState MOS6522CUDAState; + typedef struct CUDAState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - MemoryRegion mem; - /* cuda registers */ - uint8_t b; - uint8_t a; - uint8_t dirb; - uint8_t dira; - uint8_t sr; - uint8_t acr; - uint8_t pcr; - uint8_t ifr; - uint8_t ier; - uint8_t anh; ADBBusState adb_bus; - CUDATimer timers[2]; + MOS6522CUDAState *mos6522_cuda; uint32_t tick_offset; uint64_t tb_frequency; @@ -105,6 +95,7 @@ typedef struct CUDAState { uint8_t last_acr; /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */ + uint64_t sr_delay_ns; QEMUTimer *sr_delay_timer; int data_in_size; @@ -120,6 +111,18 @@ typedef struct CUDAState { QEMUTimer *adb_poll_timer; } CUDAState; +/* MOS6522 CUDA */ +typedef struct MOS6522CUDAState { + /*< private >*/ + MOS6522State parent_obj; + + CUDAState *cuda; +} MOS6522CUDAState; + +#define TYPE_MOS6522_CUDA "mos6522-cuda" +#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ + TYPE_MOS6522_CUDA) + /* MacIO */ #define TYPE_OLDWORLD_MACIO "macio-oldworld" #define TYPE_NEWWORLD_MACIO "macio-newworld" -- cgit v1.1 From 7092e84d42b1cfc2440b1dcf66cdae814fa112b3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 9 Feb 2018 18:51:41 +0000 Subject: ppc: move CUDAState and other CUDA-related definitions into separate cuda.h file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 1 + hw/misc/macio/macio.c | 1 + hw/ppc/mac.h | 77 --------------------------------------------------- 3 files changed, 2 insertions(+), 77 deletions(-) (limited to 'hw') diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 54c02ae..377b91d 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -27,6 +27,7 @@ #include "hw/ppc/mac.h" #include "hw/input/adb.h" #include "hw/misc/mos6522.h" +#include "hw/misc/macio/cuda.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index a639b09..024f855 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/mac.h" +#include "hw/misc/macio/cuda.h" #include "hw/pci/pci.h" #include "hw/ppc/mac_dbdma.h" #include "hw/char/escc.h" diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 3e9f13d..4702194 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -45,83 +45,6 @@ #define ESCC_CLOCK 3686400 -/* CUDA commands (2nd byte) */ -#define CUDA_WARM_START 0x0 -#define CUDA_AUTOPOLL 0x1 -#define CUDA_GET_6805_ADDR 0x2 -#define CUDA_GET_TIME 0x3 -#define CUDA_GET_PRAM 0x7 -#define CUDA_SET_6805_ADDR 0x8 -#define CUDA_SET_TIME 0x9 -#define CUDA_POWERDOWN 0xa -#define CUDA_POWERUP_TIME 0xb -#define CUDA_SET_PRAM 0xc -#define CUDA_MS_RESET 0xd -#define CUDA_SEND_DFAC 0xe -#define CUDA_BATTERY_SWAP_SENSE 0x10 -#define CUDA_RESET_SYSTEM 0x11 -#define CUDA_SET_IPL 0x12 -#define CUDA_FILE_SERVER_FLAG 0x13 -#define CUDA_SET_AUTO_RATE 0x14 -#define CUDA_GET_AUTO_RATE 0x16 -#define CUDA_SET_DEVICE_LIST 0x19 -#define CUDA_GET_DEVICE_LIST 0x1a -#define CUDA_SET_ONE_SECOND_MODE 0x1b -#define CUDA_SET_POWER_MESSAGES 0x21 -#define CUDA_GET_SET_IIC 0x22 -#define CUDA_WAKEUP 0x23 -#define CUDA_TIMER_TICKLE 0x24 -#define CUDA_COMBINED_FORMAT_IIC 0x25 - -/* Cuda */ -#define TYPE_CUDA "cuda" -#define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) - -typedef struct MOS6522CUDAState MOS6522CUDAState; - -typedef struct CUDAState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - MemoryRegion mem; - - ADBBusState adb_bus; - MOS6522CUDAState *mos6522_cuda; - - uint32_t tick_offset; - uint64_t tb_frequency; - - uint8_t last_b; - uint8_t last_acr; - - /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */ - uint64_t sr_delay_ns; - QEMUTimer *sr_delay_timer; - - int data_in_size; - int data_in_index; - int data_out_index; - - qemu_irq irq; - uint16_t adb_poll_mask; - uint8_t autopoll_rate_ms; - uint8_t autopoll; - uint8_t data_in[128]; - uint8_t data_out[16]; - QEMUTimer *adb_poll_timer; -} CUDAState; - -/* MOS6522 CUDA */ -typedef struct MOS6522CUDAState { - /*< private >*/ - MOS6522State parent_obj; - - CUDAState *cuda; -} MOS6522CUDAState; - -#define TYPE_MOS6522_CUDA "mos6522-cuda" -#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ - TYPE_MOS6522_CUDA) /* MacIO */ #define TYPE_OLDWORLD_MACIO "macio-oldworld" -- cgit v1.1 From 4b402e09e6aa40365a11ed0d258ac1e973d9725b Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 9 Feb 2018 18:51:42 +0000 Subject: cuda: convert to trace-events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/misc/macio/cuda.c | 50 ++++++++++++++++------------------------------ hw/misc/macio/trace-events | 11 ++++++++++ 2 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 hw/misc/macio/trace-events (limited to 'hw') diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 377b91d..bd9b862 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -32,19 +32,7 @@ #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "qemu/log.h" - -/* debug CUDA packets */ -//#define DEBUG_CUDA_PACKET - -/* debug CUDA packets */ -//#define DEBUG_CUDA_PACKET - -#ifdef DEBUG_CUDA -#define CUDA_DPRINTF(fmt, ...) \ - do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0) -#else -#define CUDA_DPRINTF(fmt, ...) -#endif +#include "trace.h" /* Bits in B data register: all active low */ #define TREQ 0x08 /* Transfer request (input) */ @@ -120,7 +108,7 @@ static void cuda_delay_set_sr_int(CUDAState *s) return; } - CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); + trace_cuda_delay_set_sr_int(); expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns; timer_mod(s->sr_delay_timer, expire); @@ -141,7 +129,7 @@ static void cuda_update(CUDAState *s) /* data output */ if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { if (s->data_out_index < sizeof(s->data_out)) { - CUDA_DPRINTF("send: %02x\n", ms->sr); + trace_cuda_data_send(ms->sr); s->data_out[s->data_out_index++] = ms->sr; cuda_delay_set_sr_int(s); } @@ -151,7 +139,7 @@ static void cuda_update(CUDAState *s) /* data input */ if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { ms->sr = s->data_in[s->data_in_index++]; - CUDA_DPRINTF("recv: %02x\n", ms->sr); + trace_cuda_data_recv(ms->sr); /* indicate end of transfer */ if (s->data_in_index >= s->data_in_size) { ms->b = (ms->b | TREQ); @@ -199,15 +187,13 @@ static void cuda_update(CUDAState *s) static void cuda_send_packet_to_host(CUDAState *s, const uint8_t *data, int len) { -#ifdef DEBUG_CUDA_PACKET - { - int i; - printf("cuda_send_packet_to_host:\n"); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + int i; + + trace_cuda_packet_send(len); + for (i = 0; i < len; i++) { + trace_cuda_packet_send_data(i, data[i]); } -#endif + memcpy(s->data_in, data, len); s->data_in_size = len; s->data_in_index = 0; @@ -411,7 +397,7 @@ static void cuda_receive_packet(CUDAState *s, for (i = 0; i < ARRAY_SIZE(handlers); i++) { const CudaCommand *desc = &handlers[i]; if (desc->command == data[0]) { - CUDA_DPRINTF("handling command %s\n", desc->name); + trace_cuda_receive_packet_cmd(desc->name); out_len = 0; if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) { cuda_send_packet_to_host(s, obuf, 3 + out_len); @@ -440,15 +426,13 @@ static void cuda_receive_packet(CUDAState *s, static void cuda_receive_packet_from_host(CUDAState *s, const uint8_t *data, int len) { -#ifdef DEBUG_CUDA_PACKET - { - int i; - printf("cuda_receive_packet_from_host:\n"); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + int i; + + trace_cuda_packet_receive(len); + for (i = 0; i < len; i++) { + trace_cuda_packet_receive_data(i, data[i]); } -#endif + switch(data[0]) { case ADB_PACKET: { diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events new file mode 100644 index 0000000..24c0a36 --- /dev/null +++ b/hw/misc/macio/trace-events @@ -0,0 +1,11 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/misc/macio/cuda.c +cuda_delay_set_sr_int(void) "" +cuda_data_send(uint8_t data) "send: 0x%02x" +cuda_data_recv(uint8_t data) "recv: 0x%02x" +cuda_receive_packet_cmd(const char *cmd) "handling command %s" +cuda_packet_receive(int len) "length %d" +cuda_packet_receive_data(int i, const uint8_t data) "[%d] 0x%02x" +cuda_packet_send(int len) "length %d" +cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" -- cgit v1.1 From 9478956794c11239b7c1c3ef9ce95c883bb839a3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 13 Feb 2018 15:37:16 -0200 Subject: hw/ppc/spapr_hcall: set htab_shift after kvmppc_resize_hpt_commit Newer kernels have a htab resize capability when adding or remove memory. At these situations, the guest kernel might reallocate its htab to a more suitable size based on the resulting memory. However, we're not setting the new value back into the machine state when a KVM guest resizes its htab. At first this doesn't seem harmful, but when migrating or saving the guest state (via virsh managedsave, for instance) this mismatch between the htab size of QEMU and the kernel makes the guest hangs when trying to load its state. Inside h_resize_hpt_commit, the hypercall that commits the hash page resize changes, let's set spapr->htab_shift to the new value if we're sure that kvmppc_resize_hpt_commit were successful. While we're here, add a "not RADIX" sanity check as it is already done in the related hypercall h_resize_hpt_prepare. Fixes: https://github.com/open-power-host-os/qemu/issues/28 Reported-by: Satheesh Rajendran Signed-off-by: Daniel Henrique Barboza Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 76422cf..1986560 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -731,11 +731,21 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, return H_AUTHORITY; } + if (!spapr->htab_shift) { + /* Radix guest, no HPT */ + return H_NOT_AVAILABLE; + } + trace_spapr_h_resize_hpt_commit(flags, shift); rc = kvmppc_resize_hpt_commit(cpu, flags, shift); if (rc != -ENOSYS) { - return resize_hpt_convert_rc(rc); + rc = resize_hpt_convert_rc(rc); + if (rc == H_SUCCESS) { + /* Need to set the new htab_shift in the machine state */ + spapr->htab_shift = shift; + } + return rc; } if (flags != 0) { -- cgit v1.1 From 2cc75c32e6fcbc1a9732a6589c7dcacf295b5b84 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 14 Feb 2018 07:35:58 +0100 Subject: hw/char: remove legacy interface escc_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move necessary stuff in escc.h and update type names. Remove slavio_serial_ms_kbd_init(). Fix code style problems reported by checkpatch.pl Update mac_newworld, mac_oldworld and sun4m to use directly the QDEV interface. Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/char/escc.c | 209 ++++++++++++++------------------------------------ hw/ppc/mac_newworld.c | 19 ++++- hw/ppc/mac_oldworld.c | 19 ++++- hw/sparc/sun4m.c | 34 +++++++- 4 files changed, 121 insertions(+), 160 deletions(-) (limited to 'hw') diff --git a/hw/char/escc.c b/hw/char/escc.c index 449bf2f..628f5f8 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -26,10 +26,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/char/escc.h" -#include "chardev/char-fe.h" -#include "chardev/char-serial.h" #include "ui/console.h" -#include "ui/input.h" #include "trace.h" /* @@ -64,53 +61,7 @@ * 2010-May-23 Artyom Tarasenko: Reworked IUS logic */ -typedef enum { - chn_a, chn_b, -} ChnID; - -#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a') - -typedef enum { - ser, kbd, mouse, -} ChnType; - -#define SERIO_QUEUE_SIZE 256 - -typedef struct { - uint8_t data[SERIO_QUEUE_SIZE]; - int rptr, wptr, count; -} SERIOQueue; - -#define SERIAL_REGS 16 -typedef struct ChannelState { - qemu_irq irq; - uint32_t rxint, txint, rxint_under_svc, txint_under_svc; - struct ChannelState *otherchn; - uint32_t reg; - uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS]; - SERIOQueue queue; - CharBackend chr; - int e0_mode, led_mode, caps_lock_mode, num_lock_mode; - int disabled; - int clock; - uint32_t vmstate_dummy; - ChnID chn; // this channel, A (base+4) or B (base+0) - ChnType type; - uint8_t rx, tx; - QemuInputHandlerState *hs; -} ChannelState; - -#define ESCC(obj) OBJECT_CHECK(ESCCState, (obj), TYPE_ESCC) - -typedef struct ESCCState { - SysBusDevice parent_obj; - - struct ChannelState chn[2]; - uint32_t it_shift; - MemoryRegion mmio; - uint32_t disabled; - uint32_t frequency; -} ESCCState; +#define CHN_C(s) ((s)->chn == escc_chn_b ? 'b' : 'a') #define SERIAL_CTRL 0 #define SERIAL_DATA 1 @@ -214,44 +165,47 @@ typedef struct ESCCState { #define R_MISC1I 14 #define R_EXTINT 15 -static void handle_kbd_command(ChannelState *s, int val); +static void handle_kbd_command(ESCCChannelState *s, int val); static int serial_can_receive(void *opaque); -static void serial_receive_byte(ChannelState *s, int ch); +static void serial_receive_byte(ESCCChannelState *s, int ch); static void clear_queue(void *opaque) { - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; + ESCCChannelState *s = opaque; + ESCCSERIOQueue *q = &s->queue; q->rptr = q->wptr = q->count = 0; } static void put_queue(void *opaque, int b) { - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; + ESCCChannelState *s = opaque; + ESCCSERIOQueue *q = &s->queue; trace_escc_put_queue(CHN_C(s), b); - if (q->count >= SERIO_QUEUE_SIZE) + if (q->count >= ESCC_SERIO_QUEUE_SIZE) { return; + } q->data[q->wptr] = b; - if (++q->wptr == SERIO_QUEUE_SIZE) + if (++q->wptr == ESCC_SERIO_QUEUE_SIZE) { q->wptr = 0; + } q->count++; serial_receive_byte(s, 0); } static uint32_t get_queue(void *opaque) { - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; + ESCCChannelState *s = opaque; + ESCCSERIOQueue *q = &s->queue; int val; if (q->count == 0) { return 0; } else { val = q->data[q->rptr]; - if (++q->rptr == SERIO_QUEUE_SIZE) + if (++q->rptr == ESCC_SERIO_QUEUE_SIZE) { q->rptr = 0; + } q->count--; } trace_escc_get_queue(CHN_C(s), val); @@ -260,7 +214,7 @@ static uint32_t get_queue(void *opaque) return val; } -static int escc_update_irq_chn(ChannelState *s) +static int escc_update_irq_chn(ESCCChannelState *s) { if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || // tx ints enabled, pending @@ -274,7 +228,7 @@ static int escc_update_irq_chn(ChannelState *s) return 0; } -static void escc_update_irq(ChannelState *s) +static void escc_update_irq(ESCCChannelState *s) { int irq; @@ -285,12 +239,12 @@ static void escc_update_irq(ChannelState *s) qemu_set_irq(s->irq, irq); } -static void escc_reset_chn(ChannelState *s) +static void escc_reset_chn(ESCCChannelState *s) { int i; s->reg = 0; - for (i = 0; i < SERIAL_REGS; i++) { + for (i = 0; i < ESCC_SERIAL_REGS; i++) { s->rregs[i] = 0; s->wregs[i] = 0; } @@ -322,13 +276,13 @@ static void escc_reset(DeviceState *d) escc_reset_chn(&s->chn[1]); } -static inline void set_rxint(ChannelState *s) +static inline void set_rxint(ESCCChannelState *s) { s->rxint = 1; - /* XXX: missing daisy chainnig: chn_b rx should have a lower priority + /* XXX: missing daisy chainnig: escc_chn_b rx should have a lower priority than chn_a rx/tx/special_condition service*/ s->rxint_under_svc = 1; - if (s->chn == chn_a) { + if (s->chn == escc_chn_a) { s->rregs[R_INTR] |= INTR_RXINTA; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; @@ -344,12 +298,12 @@ static inline void set_rxint(ChannelState *s) escc_update_irq(s); } -static inline void set_txint(ChannelState *s) +static inline void set_txint(ESCCChannelState *s) { s->txint = 1; if (!s->rxint_under_svc) { s->txint_under_svc = 1; - if (s->chn == chn_a) { + if (s->chn == escc_chn_a) { if (s->wregs[W_INTR] & INTR_TXINT) { s->rregs[R_INTR] |= INTR_TXINTA; } @@ -367,11 +321,11 @@ static inline void set_txint(ChannelState *s) } } -static inline void clr_rxint(ChannelState *s) +static inline void clr_rxint(ESCCChannelState *s) { s->rxint = 0; s->rxint_under_svc = 0; - if (s->chn == chn_a) { + if (s->chn == escc_chn_a) { if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; else @@ -389,11 +343,11 @@ static inline void clr_rxint(ChannelState *s) escc_update_irq(s); } -static inline void clr_txint(ChannelState *s) +static inline void clr_txint(ESCCChannelState *s) { s->txint = 0; s->txint_under_svc = 0; - if (s->chn == chn_a) { + if (s->chn == escc_chn_a) { if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; else @@ -412,12 +366,12 @@ static inline void clr_txint(ChannelState *s) escc_update_irq(s); } -static void escc_update_parameters(ChannelState *s) +static void escc_update_parameters(ESCCChannelState *s) { int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != ser) + if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != escc_serial) return; if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { @@ -474,7 +428,7 @@ static void escc_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { ESCCState *serial = opaque; - ChannelState *s; + ESCCChannelState *s; uint32_t saddr; int newreg, channel; @@ -561,7 +515,7 @@ static void escc_mem_write(void *opaque, hwaddr addr, /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx, 1); - } else if (s->type == kbd && !s->disabled) { + } else if (s->type == escc_kbd && !s->disabled) { handle_kbd_command(s, val); } } @@ -578,7 +532,7 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr, unsigned size) { ESCCState *serial = opaque; - ChannelState *s; + ESCCChannelState *s; uint32_t saddr; uint32_t ret; int channel; @@ -595,10 +549,11 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr, case SERIAL_DATA: s->rregs[R_STATUS] &= ~STATUS_RXAV; clr_rxint(s); - if (s->type == kbd || s->type == mouse) + if (s->type == escc_kbd || s->type == escc_mouse) { ret = get_queue(s); - else + } else { ret = s->rx; + } trace_escc_mem_readb_data(CHN_C(s), ret); qemu_chr_fe_accept_input(&s->chr); return ret; @@ -620,7 +575,7 @@ static const MemoryRegionOps escc_mem_ops = { static int serial_can_receive(void *opaque) { - ChannelState *s = opaque; + ESCCChannelState *s = opaque; int ret; if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled @@ -632,7 +587,7 @@ static int serial_can_receive(void *opaque) return ret; } -static void serial_receive_byte(ChannelState *s, int ch) +static void serial_receive_byte(ESCCChannelState *s, int ch) { trace_escc_serial_receive_byte(CHN_C(s), ch); s->rregs[R_STATUS] |= STATUS_RXAV; @@ -640,7 +595,7 @@ static void serial_receive_byte(ChannelState *s, int ch) set_rxint(s); } -static void serial_receive_break(ChannelState *s) +static void serial_receive_break(ESCCChannelState *s) { s->rregs[R_STATUS] |= STATUS_BRK; escc_update_irq(s); @@ -648,13 +603,13 @@ static void serial_receive_break(ChannelState *s) static void serial_receive1(void *opaque, const uint8_t *buf, int size) { - ChannelState *s = opaque; + ESCCChannelState *s = opaque; serial_receive_byte(s, buf[0]); } static void serial_event(void *opaque, int event) { - ChannelState *s = opaque; + ESCCChannelState *s = opaque; if (event == CHR_EVENT_BREAK) serial_receive_break(s); } @@ -664,16 +619,16 @@ static const VMStateDescription vmstate_escc_chn = { .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(vmstate_dummy, ChannelState), - VMSTATE_UINT32(reg, ChannelState), - VMSTATE_UINT32(rxint, ChannelState), - VMSTATE_UINT32(txint, ChannelState), - VMSTATE_UINT32(rxint_under_svc, ChannelState), - VMSTATE_UINT32(txint_under_svc, ChannelState), - VMSTATE_UINT8(rx, ChannelState), - VMSTATE_UINT8(tx, ChannelState), - VMSTATE_BUFFER(wregs, ChannelState), - VMSTATE_BUFFER(rregs, ChannelState), + VMSTATE_UINT32(vmstate_dummy, ESCCChannelState), + VMSTATE_UINT32(reg, ESCCChannelState), + VMSTATE_UINT32(rxint, ESCCChannelState), + VMSTATE_UINT32(txint, ESCCChannelState), + VMSTATE_UINT32(rxint_under_svc, ESCCChannelState), + VMSTATE_UINT32(txint_under_svc, ESCCChannelState), + VMSTATE_UINT8(rx, ESCCChannelState), + VMSTATE_UINT8(tx, ESCCChannelState), + VMSTATE_BUFFER(wregs, ESCCChannelState), + VMSTATE_BUFFER(rregs, ESCCChannelState), VMSTATE_END_OF_LIST() } }; @@ -684,44 +639,15 @@ static const VMStateDescription vmstate_escc = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_STRUCT_ARRAY(chn, ESCCState, 2, 2, vmstate_escc_chn, - ChannelState), + ESCCChannelState), VMSTATE_END_OF_LIST() } }; -MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, - Chardev *chrA, Chardev *chrB, - int clock, int it_shift) -{ - DeviceState *dev; - SysBusDevice *s; - ESCCState *d; - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", clock); - qdev_prop_set_uint32(dev, "it_shift", it_shift); - qdev_prop_set_chr(dev, "chrB", chrB); - qdev_prop_set_chr(dev, "chrA", chrA); - qdev_prop_set_uint32(dev, "chnBtype", ser); - qdev_prop_set_uint32(dev, "chnAtype", ser); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irqB); - sysbus_connect_irq(s, 1, irqA); - if (base) { - sysbus_mmio_map(s, 0, base); - } - - d = ESCC(s); - return &d->mmio; -} - - static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { - ChannelState *s = (ChannelState *)dev; + ESCCChannelState *s = (ESCCChannelState *)dev; int qcode, keycode; InputKeyEvent *key; @@ -777,7 +703,7 @@ static QemuInputHandler sunkbd_handler = { .event = sunkbd_handle_event, }; -static void handle_kbd_command(ChannelState *s, int val) +static void handle_kbd_command(ESCCChannelState *s, int val) { trace_escc_kbd_command(val); if (s->led_mode) { // Ignore led byte @@ -808,7 +734,7 @@ static void handle_kbd_command(ChannelState *s, int val) static void sunmouse_event(void *opaque, int dx, int dy, int dz, int buttons_state) { - ChannelState *s = opaque; + ESCCChannelState *s = opaque; int ch; trace_escc_sunmouse_event(dx, dy, buttons_state); @@ -847,27 +773,6 @@ static void sunmouse_event(void *opaque, put_queue(s, 0); } -void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, - int disabled, int clock, int it_shift) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", disabled); - qdev_prop_set_uint32(dev, "frequency", clock); - qdev_prop_set_uint32(dev, "it_shift", it_shift); - qdev_prop_set_chr(dev, "chrB", NULL); - qdev_prop_set_chr(dev, "chrA", NULL); - qdev_prop_set_uint32(dev, "chnBtype", mouse); - qdev_prop_set_uint32(dev, "chnAtype", kbd); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_connect_irq(s, 1, irq); - sysbus_mmio_map(s, 0, base); -} - static void escc_init1(Object *obj) { ESCCState *s = ESCC(obj); @@ -904,11 +809,11 @@ static void escc_realize(DeviceState *dev, Error **errp) } } - if (s->chn[0].type == mouse) { + if (s->chn[0].type == escc_mouse) { qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0, "QEMU Sun Mouse"); } - if (s->chn[1].type == kbd) { + if (s->chn[1].type == escc_kbd) { s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]), &sunkbd_handler); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index b832417..4e1298e 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -369,8 +369,23 @@ static void ppc_core99_init(MachineState *machine) } /* init basic PC hardware */ - escc_mem = escc_init(0, pic[0x25], pic[0x24], - serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); + + dev = qdev_create(NULL, TYPE_ESCC); + qdev_prop_set_uint32(dev, "disabled", 0); + qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); + qdev_prop_set_uint32(dev, "it_shift", 4); + qdev_prop_set_chr(dev, "chrA", serial_hds[0]); + qdev_prop_set_chr(dev, "chrB", serial_hds[1]); + qdev_prop_set_uint32(dev, "chnAtype", escc_serial); + qdev_prop_set_uint32(dev, "chnBtype", escc_serial); + qdev_init_nofail(dev); + + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, pic[0x24]); + sysbus_connect_irq(s, 1, pic[0x25]); + + escc_mem = &ESCC(s)->mmio; + memory_region_init_alias(escc_bar, NULL, "escc-bar", escc_mem, 0, memory_region_size(escc_mem)); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d1f4546..d0d21d2 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -104,6 +104,7 @@ static void ppc_heathrow_init(MachineState *machine) DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; uint64_t tbfreq; + SysBusDevice *s; linux_boot = (kernel_filename != NULL); @@ -264,8 +265,22 @@ static void ppc_heathrow_init(MachineState *machine) get_system_io()); pci_vga_init(pci_bus); - escc_mem = escc_init(0, pic[0x0f], pic[0x10], serial_hds[0], - serial_hds[1], ESCC_CLOCK, 4); + dev = qdev_create(NULL, TYPE_ESCC); + qdev_prop_set_uint32(dev, "disabled", 0); + qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); + qdev_prop_set_uint32(dev, "it_shift", 4); + qdev_prop_set_chr(dev, "chrA", serial_hds[0]); + qdev_prop_set_chr(dev, "chrB", serial_hds[1]); + qdev_prop_set_uint32(dev, "chnBtype", escc_serial); + qdev_prop_set_uint32(dev, "chnAtype", escc_serial); + qdev_init_nofail(dev); + + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, pic[0x10]); + sysbus_connect_irq(s, 1, pic[0x0f]); + + escc_mem = &ESCC(s)->mmio; + memory_region_init_alias(escc_bar, NULL, "escc-bar", escc_mem, 0, memory_region_size(escc_mem)); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index f9892e3..61eb424 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -818,6 +818,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, DriveInfo *fd[MAX_FD]; FWCfgState *fw_cfg; unsigned int num_vsimms; + DeviceState *dev; + SysBusDevice *s; /* init CPUs */ for(i = 0; i < smp_cpus; i++) { @@ -925,12 +927,36 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus); - slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[14], - !machine->enable_graphics, ESCC_CLOCK, 1); /* Slavio TTYA (base+4, Linux ttyS0) is the first QEMU serial device Slavio TTYB (base+0, Linux ttyS1) is the second QEMU serial device */ - escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15], - serial_hds[0], serial_hds[1], ESCC_CLOCK, 1); + dev = qdev_create(NULL, TYPE_ESCC); + qdev_prop_set_uint32(dev, "disabled", !machine->enable_graphics); + qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); + qdev_prop_set_uint32(dev, "it_shift", 1); + qdev_prop_set_chr(dev, "chrB", NULL); + qdev_prop_set_chr(dev, "chrA", NULL); + qdev_prop_set_uint32(dev, "chnBtype", escc_mouse); + qdev_prop_set_uint32(dev, "chnAtype", escc_kbd); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, slavio_irq[14]); + sysbus_connect_irq(s, 1, slavio_irq[14]); + sysbus_mmio_map(s, 0, hwdef->ms_kb_base); + + dev = qdev_create(NULL, TYPE_ESCC); + qdev_prop_set_uint32(dev, "disabled", 0); + qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); + qdev_prop_set_uint32(dev, "it_shift", 1); + qdev_prop_set_chr(dev, "chrB", serial_hds[1]); + qdev_prop_set_chr(dev, "chrA", serial_hds[0]); + qdev_prop_set_uint32(dev, "chnBtype", escc_serial); + qdev_prop_set_uint32(dev, "chnAtype", escc_serial); + qdev_init_nofail(dev); + + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, slavio_irq[15]); + sysbus_connect_irq(s, 1, slavio_irq[15]); + sysbus_mmio_map(s, 0, hwdef->serial_base); if (hwdef->apc_base) { apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); -- cgit v1.1 From 8c5909c41916f25b47bfdc465059a926603c1319 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Thu, 15 Feb 2018 11:44:41 +1100 Subject: ppc/spapr-caps: Change migration macro to take full spapr-cap name Change the macro that generates the vmstate migration field and the needed function for the spapr-caps to take the full spapr-cap name. This has the benefit of meaning this instance will be picked up when greping for the spapr-caps and making it more obvious what this macro is doing. Signed-off-by: Suraj Jitindar Singh Signed-off-by: David Gibson --- hw/ppc/spapr_caps.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 62efdae..e69d308 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -350,34 +350,34 @@ int spapr_caps_post_migration(sPAPRMachineState *spapr) } /* Used to generate the migration field and needed function for a spapr cap */ -#define SPAPR_CAP_MIG_STATE(cap, ccap) \ -static bool spapr_cap_##cap##_needed(void *opaque) \ +#define SPAPR_CAP_MIG_STATE(sname, cap) \ +static bool spapr_cap_##sname##_needed(void *opaque) \ { \ sPAPRMachineState *spapr = opaque; \ \ - return spapr->cmd_line_caps[SPAPR_CAP_##ccap] && \ - (spapr->eff.caps[SPAPR_CAP_##ccap] != \ - spapr->def.caps[SPAPR_CAP_##ccap]); \ + return spapr->cmd_line_caps[cap] && \ + (spapr->eff.caps[cap] != \ + spapr->def.caps[cap]); \ } \ \ -const VMStateDescription vmstate_spapr_cap_##cap = { \ - .name = "spapr/cap/" #cap, \ +const VMStateDescription vmstate_spapr_cap_##sname = { \ + .name = "spapr/cap/" #sname, \ .version_id = 1, \ .minimum_version_id = 1, \ - .needed = spapr_cap_##cap##_needed, \ + .needed = spapr_cap_##sname##_needed, \ .fields = (VMStateField[]) { \ - VMSTATE_UINT8(mig.caps[SPAPR_CAP_##ccap], \ + VMSTATE_UINT8(mig.caps[cap], \ sPAPRMachineState), \ VMSTATE_END_OF_LIST() \ }, \ } -SPAPR_CAP_MIG_STATE(htm, HTM); -SPAPR_CAP_MIG_STATE(vsx, VSX); -SPAPR_CAP_MIG_STATE(dfp, DFP); -SPAPR_CAP_MIG_STATE(cfpc, CFPC); -SPAPR_CAP_MIG_STATE(sbbc, SBBC); -SPAPR_CAP_MIG_STATE(ibs, IBS); +SPAPR_CAP_MIG_STATE(htm, SPAPR_CAP_HTM); +SPAPR_CAP_MIG_STATE(vsx, SPAPR_CAP_VSX); +SPAPR_CAP_MIG_STATE(dfp, SPAPR_CAP_DFP); +SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC); +SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); +SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); void spapr_caps_reset(sPAPRMachineState *spapr) { -- cgit v1.1 From 72194664c8a16b67865eb95054f984dd169cfa86 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 14 Feb 2018 20:40:26 +0100 Subject: spapr: use spapr->vsmt to compute VCPU ids Since the introduction of VSMT in 2.11, the spacing of VCPU ids between cores is controllable through a machine property instead of being only dictated by the SMT mode of the host: cpu->vcpu_id = (cc->core_id * spapr->vsmt / smp_threads) + i Until recently, the machine code would try to change the SMT mode of the host to be equal to VSMT or exit. This allowed the rest of the code to assume that kvmppc_smt_threads() == spapr->vsmt is always true. Recent commit "8904e5a75005 spapr: Adjust default VSMT value for better migration compatibility" relaxed the rule. If the VSMT mode cannot be set in KVM for some reasons, but the requested CPU topology is compatible with the current SMT mode, then we let the guest run with kvmppc_smt_threads() != spapr->vsmt. This breaks quite a few places in the code, in particular when calculating DRC indexes. This is what happens on a POWER host with subcores-per-core=2 (ie, supports up to SMT4) when passing the following topology: -smp threads=4,maxcpus=16 \ -device host-spapr-cpu-core,core-id=4,id=core1 \ -device host-spapr-cpu-core,core-id=8,id=core2 qemu-system-ppc64: warning: Failed to set KVM's VSMT mode to 8 (errno -22) This is expected since KVM is limited to SMT4, but the guest is started anyway because this topology can run on SMT4 even with a VSMT8 spacing. But when we look at the DT, things get nastier: cpus { ... ibm,drc-indexes = <0x4 0x10000000 0x10000004 0x10000008 0x1000000c>; This means that we have the following association: CPU core device | DRC | VCPU id -----------------+------------+--------- boot core | 0x10000000 | 0 core1 | 0x10000004 | 4 core2 | 0x10000008 | 8 core3 | 0x1000000c | 12 But since the spacing of VCPU ids is 8, the DRC for core1 points to a VCPU that doesn't exist, the DRC for core2 points to the first VCPU of core1 and and so on... ... PowerPC,POWER8@0 { ... ibm,my-drc-index = <0x10000000>; ... }; PowerPC,POWER8@8 { ... ibm,my-drc-index = <0x10000008>; ... }; PowerPC,POWER8@10 { ... No ibm,my-drc-index property for this core since 0x10000010 doesn't exist in ibm,drc-indexes above. ... }; }; ... interrupt-controller { ... ibm,interrupt-server-ranges = <0x0 0x10>; With a spacing of 8, the highest VCPU id for the given topology should be: 16 * 8 / 4 = 32 and not 16 ... linux,phandle = <0x7e7323b8>; interrupt-controller; }; And CPU hot-plug/unplug is broken: (qemu) device_del core1 pseries-hotplug-cpu: Cannot find CPU (drc index 10000004) to remove (qemu) device_del core2 cpu 4 (hwid 8) Ready to die... cpu 5 (hwid 9) Ready to die... cpu 6 (hwid 10) Ready to die... cpu 7 (hwid 11) Ready to die... These are the VCPU ids of core1 actually (qemu) device_add host-spapr-cpu-core,core-id=12,id=core3 (qemu) device_del core3 pseries-hotplug-cpu: Cannot find CPU (drc index 1000000c) to remove This patches all the code in hw/ppc/spapr.c to assume the VSMT spacing when manipulating VCPU ids. Fixes: 8904e5a75005 Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 9f29434..ea7429c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -160,9 +160,9 @@ static void pre_2_10_vmstate_unregister_dummy_icp(int i) (void *)(uintptr_t) i); } -static inline int xics_max_server_number(void) +static int xics_max_server_number(sPAPRMachineState *spapr) { - return DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads); + return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads); } static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) @@ -194,7 +194,7 @@ static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) if (smc->pre_2_10_has_unused_icps) { int i; - for (i = 0; i < xics_max_server_number(); i++) { + for (i = 0; i < xics_max_server_number(spapr); i++) { /* Dummy entries get deregistered when real ICPState objects * are registered during CPU core hotplug. */ @@ -337,7 +337,6 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) int ret = 0, offset, cpus_offset; CPUState *cs; char cpu_model[32]; - int smt = kvmppc_smt_threads(); uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; CPU_FOREACH(cs) { @@ -346,7 +345,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) int index = spapr_vcpu_id(cpu); int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); - if ((index % smt) != 0) { + if (index % spapr->vsmt != 0) { continue; } @@ -614,7 +613,6 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) CPUState *cs; int cpus_offset; char *nodename; - int smt = kvmppc_smt_threads(); cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); _FDT(cpus_offset); @@ -632,7 +630,7 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) DeviceClass *dc = DEVICE_GET_CLASS(cs); int offset; - if ((index % smt) != 0) { + if (index % spapr->vsmt != 0) { continue; } @@ -1131,7 +1129,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); /* /interrupt controller */ - spapr_dt_xics(xics_max_server_number(), fdt, PHANDLE_XICP); + spapr_dt_xics(xics_max_server_number(spapr), fdt, PHANDLE_XICP); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -2224,7 +2222,6 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); const char *type = spapr_get_cpu_core_type(machine->cpu_type); - int smt = kvmppc_smt_threads(); const CPUArchIdList *possible_cpus; int boot_cores_nr = smp_cpus / smp_threads; int i; @@ -2254,7 +2251,7 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) if (mc->has_hotpluggable_cpus) { spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, - (core_id / smp_threads) * smt); + (core_id / smp_threads) * spapr->vsmt); } if (i < boot_cores_nr) { @@ -3281,10 +3278,10 @@ static void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); int index; sPAPRDRConnector *drc; CPUCore *cc = CPU_CORE(dev); - int smt = kvmppc_smt_threads(); if (!spapr_find_cpu_slot(MACHINE(hotplug_dev), cc->core_id, &index)) { error_setg(errp, "Unable to find CPU core with core-id: %d", @@ -3296,7 +3293,7 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * smt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * spapr->vsmt); g_assert(drc); spapr_drc_detach(drc); @@ -3315,7 +3312,6 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, CPUState *cs = CPU(core->threads[0]); sPAPRDRConnector *drc; Error *local_err = NULL; - int smt = kvmppc_smt_threads(); CPUArchId *core_slot; int index; bool hotplugged = spapr_drc_hotplugged(dev); @@ -3326,7 +3322,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, cc->core_id); return; } - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * smt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * spapr->vsmt); g_assert(drc || !mc->has_hotpluggable_cpus); -- cgit v1.1 From 648edb64751ea0e550f36302fa66f9f11e480824 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 14 Feb 2018 20:40:35 +0100 Subject: spapr: move VCPU calculation to core machine code The VCPU ids are currently computed and assigned to each individual CPU threads in spapr_cpu_core_realize(). But the numbering logic of VCPU ids is actually a machine-level concept, and many places in hw/ppc/spapr.c also have to compute VCPU ids out of CPU indexes. The current formula used in spapr_cpu_core_realize() is: vcpu_id = (cc->core_id * spapr->vsmt / smp_threads) + i where: cc->core_id is a multiple of smp_threads cpu_index = cc->core_id + i 0 <= i < smp_threads So we have: cpu_index % smp_threads == i cc->core_id / smp_threads == cpu_index / smp_threads hence: vcpu_id = (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; This formula was used before VSMT at the time VCPU ids where computed at the target emulation level. It has the advantage of being useable to derive a VPCU id out of a CPU index only. It is fitted for all the places where the machine code has to compute a VCPU id. This patch introduces an accessor to set the VCPU id in a PowerPCCPU object using the above formula. It is a first step to consolidate all the VCPU id logic in a single place. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 19 +++++++++++++++++++ hw/ppc/spapr_cpu_core.c | 9 ++------- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ea7429c..30cc48f 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3802,6 +3802,25 @@ int spapr_vcpu_id(PowerPCCPU *cpu) } } +void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + int vcpu_id; + + vcpu_id = + (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; + + if (kvm_enabled() && !kvm_vcpu_id_is_valid(vcpu_id)) { + error_setg(errp, "Can't create CPU with id %d in KVM", vcpu_id); + error_append_hint(errp, "Adjust the number of cpus to %d " + "or try to raise the number of threads per core\n", + vcpu_id * smp_threads / spapr->vsmt); + return; + } + + cpu->vcpu_id = vcpu_id; +} + PowerPCCPU *spapr_find_cpu(int vcpu_id) { CPUState *cs; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 590d167..94afeb3 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -172,13 +172,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) cs = CPU(obj); cpu = sc->threads[i] = POWERPC_CPU(obj); cs->cpu_index = cc->core_id + i; - cpu->vcpu_id = (cc->core_id * spapr->vsmt / smp_threads) + i; - if (kvm_enabled() && !kvm_vcpu_id_is_valid(cpu->vcpu_id)) { - error_setg(&local_err, "Can't create CPU with id %d in KVM", - cpu->vcpu_id); - error_append_hint(&local_err, "Adjust the number of cpus to %d " - "or try to raise the number of threads per core\n", - cpu->vcpu_id * smp_threads / spapr->vsmt); + spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + if (local_err) { goto err; } -- cgit v1.1 From 14bb4486c819ea797a151b3e0fe53d6f5c7b3fc5 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 14 Feb 2018 20:40:44 +0100 Subject: spapr: rename spapr_vcpu_id() to spapr_get_vcpu_id() The spapr_vcpu_id() function is an accessor actually. Let's rename it for symmetry with the recently added spapr_set_vcpu_id() helper. The motivation behind this is that a later patch will consolidate the VCPU id formula in a function and spapr_vcpu_id looks like an appropriate name. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 30cc48f..18ebc05 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -209,7 +209,7 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, int i, ret = 0; uint32_t servers_prop[smt_threads]; uint32_t gservers_prop[smt_threads * 2]; - int index = spapr_vcpu_id(cpu); + int index = spapr_get_vcpu_id(cpu); if (cpu->compat_pvr) { ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->compat_pvr); @@ -238,7 +238,7 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) { - int index = spapr_vcpu_id(cpu); + int index = spapr_get_vcpu_id(cpu); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), @@ -342,7 +342,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs); - int index = spapr_vcpu_id(cpu); + int index = spapr_get_vcpu_id(cpu); int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); if (index % spapr->vsmt != 0) { @@ -492,7 +492,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = spapr_vcpu_id(cpu); + int index = spapr_get_vcpu_id(cpu); uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() @@ -626,7 +626,7 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) */ CPU_FOREACH_REVERSE(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = spapr_vcpu_id(cpu); + int index = spapr_get_vcpu_id(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); int offset; @@ -3234,7 +3234,7 @@ static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, { PowerPCCPU *cpu = POWERPC_CPU(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs); - int id = spapr_vcpu_id(cpu); + int id = spapr_get_vcpu_id(cpu); void *fdt; int offset, fdt_size; char *nodename; @@ -3791,7 +3791,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, ics_pic_print_info(spapr->ics, mon); } -int spapr_vcpu_id(PowerPCCPU *cpu) +int spapr_get_vcpu_id(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -3828,7 +3828,7 @@ PowerPCCPU *spapr_find_cpu(int vcpu_id) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - if (spapr_vcpu_id(cpu) == vcpu_id) { + if (spapr_get_vcpu_id(cpu) == vcpu_id) { return cpu; } } -- cgit v1.1 From 5d0fb1508e2d279da74ef4a103e8def9b52c6304 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 14 Feb 2018 20:40:53 +0100 Subject: spapr: consolidate the VCPU id numbering logic in a single place Several places in the code need to calculate a VCPU id: (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads (core_id / smp_threads) * spapr->vsmt (1 user) index * spapr->vsmt (2 users) or guess that the VCPU id of a given VCPU is the first thread of a virtual core: index % spapr->vsmt != 0 Even if the numbering logic isn't that complex, it is rather fragile to have these assumptions open-coded in several places. FWIW this was proved with recent issues related to VSMT. This patch moves the VCPU id formula to a single function to be called everywhere the code needs to compute one. It also adds an helper to guess if a VCPU is the first thread of a VCORE. Signed-off-by: Greg Kurz [dwg: Rename spapr_is_vcore() to spapr_is_thread0_in_vcore() for clarity] Signed-off-by: David Gibson --- hw/ppc/spapr.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 18ebc05..83c9d66 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -99,6 +99,21 @@ #define PHANDLE_XICP 0x00001111 +/* These two functions implement the VCPU id numbering: one to compute them + * all and one to identify thread 0 of a VCORE. Any change to the first one + * is likely to have an impact on the second one, so let's keep them close. + */ +static int spapr_vcpu_id(sPAPRMachineState *spapr, int cpu_index) +{ + return + (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; +} +static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr, + PowerPCCPU *cpu) +{ + return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0; +} + static ICSState *spapr_ics_create(sPAPRMachineState *spapr, const char *type_ics, int nr_irqs, Error **errp) @@ -345,7 +360,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) int index = spapr_get_vcpu_id(cpu); int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); - if (index % spapr->vsmt != 0) { + if (!spapr_is_thread0_in_vcore(spapr, cpu)) { continue; } @@ -630,7 +645,7 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) DeviceClass *dc = DEVICE_GET_CLASS(cs); int offset; - if (index % spapr->vsmt != 0) { + if (!spapr_is_thread0_in_vcore(spapr, cpu)) { continue; } @@ -2251,7 +2266,7 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) if (mc->has_hotpluggable_cpus) { spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, - (core_id / smp_threads) * spapr->vsmt); + spapr_vcpu_id(spapr, core_id)); } if (i < boot_cores_nr) { @@ -3293,7 +3308,8 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * spapr->vsmt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, + spapr_vcpu_id(spapr, cc->core_id)); g_assert(drc); spapr_drc_detach(drc); @@ -3322,7 +3338,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, cc->core_id); return; } - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * spapr->vsmt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, + spapr_vcpu_id(spapr, cc->core_id)); g_assert(drc || !mc->has_hotpluggable_cpus); @@ -3807,8 +3824,7 @@ void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp) sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); int vcpu_id; - vcpu_id = - (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; + vcpu_id = spapr_vcpu_id(spapr, cpu_index); if (kvm_enabled() && !kvm_vcpu_id_is_valid(vcpu_id)) { error_setg(errp, "Can't create CPU with id %d in KVM", vcpu_id); -- cgit v1.1 From 4f5b039d2bf9bb26b6e26a3dc65da36fe970cba9 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Fri, 16 Feb 2018 13:33:27 +1100 Subject: ppc/spapr-caps: Disallow setting workaround for spapr-cap-ibs The spapr-cap cap-ibs can only have values broken or fixed as there is no explicit workaround required. Currently setting the value workaround for this cap will hit an assert if the guest makes the hcall h_get_cpu_characteristics. Report an error when attempting to apply the setting with a more helpful error message. Reported-by: Satheesh Rajendran Signed-off-by: Suraj Jitindar Singh Signed-off-by: David Gibson --- hw/ppc/spapr_caps.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index e69d308..99a4b71 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -205,7 +205,9 @@ static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { - if (tcg_enabled() && val) { + if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */ + error_setg(errp, "Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=fixed"); + } else if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) { @@ -263,7 +265,7 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { }, [SPAPR_CAP_IBS] = { .name = "ibs", - .description = "Indirect Branch Serialisation" VALUE_DESC_TRISTATE, + .description = "Indirect Branch Serialisation (broken, fixed)", .index = SPAPR_CAP_IBS, .get = spapr_cap_get_tristate, .set = spapr_cap_set_tristate, -- cgit v1.1 From 58d5b22bbd505dc942d137d5d3da89ad9bc16c0a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 15 Feb 2018 22:27:06 +0100 Subject: ppc4xx: Add device models found in PPC440 core SoCs These devices are found in newer SoCs based on 440 core e.g. the 460EX (http://www.embeddeddeveloper.com/assets/processors/amcc/datasheets/ PP460EX_DS2063.pdf) Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/ppc/ppc440.h | 26 ++ hw/ppc/ppc440_uc.c | 1159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1185 insertions(+) create mode 100644 hw/ppc/ppc440.h create mode 100644 hw/ppc/ppc440_uc.c (limited to 'hw') diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h new file mode 100644 index 0000000..ad27db1 --- /dev/null +++ b/hw/ppc/ppc440.h @@ -0,0 +1,26 @@ +/* + * QEMU PowerPC 440 shared definitions + * + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2018 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#ifndef PPC440_H +#define PPC440_H + +#include "hw/ppc/ppc.h" + +void ppc4xx_l2sram_init(CPUPPCState *env); +void ppc4xx_cpr_init(CPUPPCState *env); +void ppc4xx_sdr_init(CPUPPCState *env); +void ppc440_sdram_init(CPUPPCState *env, int nbanks, + MemoryRegion *ram_memories, + hwaddr *ram_bases, hwaddr *ram_sizes, + int do_init); +void ppc4xx_ahb_init(CPUPPCState *env); +void ppc460ex_pcie_init(CPUPPCState *env); + +#endif /* PPC440_H */ diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c new file mode 100644 index 0000000..4e2523a --- /dev/null +++ b/hw/ppc/ppc440_uc.c @@ -0,0 +1,1159 @@ +/* + * QEMU PowerPC 440 embedded processors emulation + * + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2018 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/hw.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "hw/ppc/ppc.h" +#include "hw/pci/pci.h" +#include "sysemu/block-backend.h" +#include "hw/ppc/ppc440.h" + +/*****************************************************************************/ +/* L2 Cache as SRAM */ +/* FIXME:fix names */ +enum { + DCR_L2CACHE_BASE = 0x30, + DCR_L2CACHE_CFG = DCR_L2CACHE_BASE, + DCR_L2CACHE_CMD, + DCR_L2CACHE_ADDR, + DCR_L2CACHE_DATA, + DCR_L2CACHE_STAT, + DCR_L2CACHE_CVER, + DCR_L2CACHE_SNP0, + DCR_L2CACHE_SNP1, + DCR_L2CACHE_END = DCR_L2CACHE_SNP1, +}; + +/* base is 460ex-specific, cf. U-Boot, ppc4xx-isram.h */ +enum { + DCR_ISRAM0_BASE = 0x20, + DCR_ISRAM0_SB0CR = DCR_ISRAM0_BASE, + DCR_ISRAM0_SB1CR, + DCR_ISRAM0_SB2CR, + DCR_ISRAM0_SB3CR, + DCR_ISRAM0_BEAR, + DCR_ISRAM0_BESR0, + DCR_ISRAM0_BESR1, + DCR_ISRAM0_PMEG, + DCR_ISRAM0_CID, + DCR_ISRAM0_REVID, + DCR_ISRAM0_DPC, + DCR_ISRAM0_END = DCR_ISRAM0_DPC +}; + +enum { + DCR_ISRAM1_BASE = 0xb0, + DCR_ISRAM1_SB0CR = DCR_ISRAM1_BASE, + /* single bank */ + DCR_ISRAM1_BEAR = DCR_ISRAM1_BASE + 0x04, + DCR_ISRAM1_BESR0, + DCR_ISRAM1_BESR1, + DCR_ISRAM1_PMEG, + DCR_ISRAM1_CID, + DCR_ISRAM1_REVID, + DCR_ISRAM1_DPC, + DCR_ISRAM1_END = DCR_ISRAM1_DPC +}; + +typedef struct ppc4xx_l2sram_t { + MemoryRegion bank[4]; + uint32_t l2cache[8]; + uint32_t isram0[11]; +} ppc4xx_l2sram_t; + +#ifdef MAP_L2SRAM +static void l2sram_update_mappings(ppc4xx_l2sram_t *l2sram, + uint32_t isarc, uint32_t isacntl, + uint32_t dsarc, uint32_t dsacntl) +{ + if (l2sram->isarc != isarc || + (l2sram->isacntl & 0x80000000) != (isacntl & 0x80000000)) { + if (l2sram->isacntl & 0x80000000) { + /* Unmap previously assigned memory region */ + memory_region_del_subregion(get_system_memory(), + &l2sram->isarc_ram); + } + if (isacntl & 0x80000000) { + /* Map new instruction memory region */ + memory_region_add_subregion(get_system_memory(), isarc, + &l2sram->isarc_ram); + } + } + if (l2sram->dsarc != dsarc || + (l2sram->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) { + if (l2sram->dsacntl & 0x80000000) { + /* Beware not to unmap the region we just mapped */ + if (!(isacntl & 0x80000000) || l2sram->dsarc != isarc) { + /* Unmap previously assigned memory region */ + memory_region_del_subregion(get_system_memory(), + &l2sram->dsarc_ram); + } + } + if (dsacntl & 0x80000000) { + /* Beware not to remap the region we just mapped */ + if (!(isacntl & 0x80000000) || dsarc != isarc) { + /* Map new data memory region */ + memory_region_add_subregion(get_system_memory(), dsarc, + &l2sram->dsarc_ram); + } + } + } +} +#endif + +static uint32_t dcr_read_l2sram(void *opaque, int dcrn) +{ + ppc4xx_l2sram_t *l2sram = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case DCR_L2CACHE_CFG: + case DCR_L2CACHE_CMD: + case DCR_L2CACHE_ADDR: + case DCR_L2CACHE_DATA: + case DCR_L2CACHE_STAT: + case DCR_L2CACHE_CVER: + case DCR_L2CACHE_SNP0: + case DCR_L2CACHE_SNP1: + ret = l2sram->l2cache[dcrn - DCR_L2CACHE_BASE]; + break; + + case DCR_ISRAM0_SB0CR: + case DCR_ISRAM0_SB1CR: + case DCR_ISRAM0_SB2CR: + case DCR_ISRAM0_SB3CR: + case DCR_ISRAM0_BEAR: + case DCR_ISRAM0_BESR0: + case DCR_ISRAM0_BESR1: + case DCR_ISRAM0_PMEG: + case DCR_ISRAM0_CID: + case DCR_ISRAM0_REVID: + case DCR_ISRAM0_DPC: + ret = l2sram->isram0[dcrn - DCR_ISRAM0_BASE]; + break; + + default: + break; + } + + return ret; +} + +static void dcr_write_l2sram(void *opaque, int dcrn, uint32_t val) +{ + /*ppc4xx_l2sram_t *l2sram = opaque;*/ + /* FIXME: Actually handle L2 cache mapping */ + + switch (dcrn) { + case DCR_L2CACHE_CFG: + case DCR_L2CACHE_CMD: + case DCR_L2CACHE_ADDR: + case DCR_L2CACHE_DATA: + case DCR_L2CACHE_STAT: + case DCR_L2CACHE_CVER: + case DCR_L2CACHE_SNP0: + case DCR_L2CACHE_SNP1: + /*l2sram->l2cache[dcrn - DCR_L2CACHE_BASE] = val;*/ + break; + + case DCR_ISRAM0_SB0CR: + case DCR_ISRAM0_SB1CR: + case DCR_ISRAM0_SB2CR: + case DCR_ISRAM0_SB3CR: + case DCR_ISRAM0_BEAR: + case DCR_ISRAM0_BESR0: + case DCR_ISRAM0_BESR1: + case DCR_ISRAM0_PMEG: + case DCR_ISRAM0_CID: + case DCR_ISRAM0_REVID: + case DCR_ISRAM0_DPC: + /*l2sram->isram0[dcrn - DCR_L2CACHE_BASE] = val;*/ + break; + + case DCR_ISRAM1_SB0CR: + case DCR_ISRAM1_BEAR: + case DCR_ISRAM1_BESR0: + case DCR_ISRAM1_BESR1: + case DCR_ISRAM1_PMEG: + case DCR_ISRAM1_CID: + case DCR_ISRAM1_REVID: + case DCR_ISRAM1_DPC: + /*l2sram->isram1[dcrn - DCR_L2CACHE_BASE] = val;*/ + break; + } + /*l2sram_update_mappings(l2sram, isarc, isacntl, dsarc, dsacntl);*/ +} + +static void l2sram_reset(void *opaque) +{ + ppc4xx_l2sram_t *l2sram = opaque; + + memset(l2sram->l2cache, 0, sizeof(l2sram->l2cache)); + l2sram->l2cache[DCR_L2CACHE_STAT - DCR_L2CACHE_BASE] = 0x80000000; + memset(l2sram->isram0, 0, sizeof(l2sram->isram0)); + /*l2sram_update_mappings(l2sram, isarc, isacntl, dsarc, dsacntl);*/ +} + +void ppc4xx_l2sram_init(CPUPPCState *env) +{ + ppc4xx_l2sram_t *l2sram; + + l2sram = g_malloc0(sizeof(*l2sram)); + /* XXX: Size is 4*64kB for 460ex, cf. U-Boot, ppc4xx-isram.h */ + memory_region_init_ram(&l2sram->bank[0], NULL, "ppc4xx.l2sram_bank0", + 64 * K_BYTE, &error_abort); + memory_region_init_ram(&l2sram->bank[1], NULL, "ppc4xx.l2sram_bank1", + 64 * K_BYTE, &error_abort); + memory_region_init_ram(&l2sram->bank[2], NULL, "ppc4xx.l2sram_bank2", + 64 * K_BYTE, &error_abort); + memory_region_init_ram(&l2sram->bank[3], NULL, "ppc4xx.l2sram_bank3", + 64 * K_BYTE, &error_abort); + qemu_register_reset(&l2sram_reset, l2sram); + ppc_dcr_register(env, DCR_L2CACHE_CFG, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_CMD, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_ADDR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_DATA, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_STAT, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_CVER, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_SNP0, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_L2CACHE_SNP1, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + + ppc_dcr_register(env, DCR_ISRAM0_SB0CR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM0_SB1CR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM0_SB2CR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM0_SB3CR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM0_PMEG, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM0_DPC, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + + ppc_dcr_register(env, DCR_ISRAM1_SB0CR, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM1_PMEG, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); + ppc_dcr_register(env, DCR_ISRAM1_DPC, + l2sram, &dcr_read_l2sram, &dcr_write_l2sram); +} + +/*****************************************************************************/ +/* Clocking Power on Reset */ +enum { + CPR0_CFGADDR = 0xC, + CPR0_CFGDATA = 0xD, + + CPR0_PLLD = 0x060, + CPR0_PLBED = 0x080, + CPR0_OPBD = 0x0C0, + CPR0_PERD = 0x0E0, + CPR0_AHBD = 0x100, +}; + +typedef struct ppc4xx_cpr_t { + uint32_t addr; +} ppc4xx_cpr_t; + +static uint32_t dcr_read_cpr(void *opaque, int dcrn) +{ + ppc4xx_cpr_t *cpr = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case CPR0_CFGADDR: + ret = cpr->addr; + break; + case CPR0_CFGDATA: + switch (cpr->addr) { + case CPR0_PLLD: + ret = (0xb5 << 24) | (1 << 16) | (9 << 8); + break; + case CPR0_PLBED: + ret = (5 << 24); + break; + case CPR0_OPBD: + ret = (2 << 24); + break; + case CPR0_PERD: + case CPR0_AHBD: + ret = (1 << 24); + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +static void dcr_write_cpr(void *opaque, int dcrn, uint32_t val) +{ + ppc4xx_cpr_t *cpr = opaque; + + switch (dcrn) { + case CPR0_CFGADDR: + cpr->addr = val; + break; + case CPR0_CFGDATA: + break; + default: + break; + } +} + +static void ppc4xx_cpr_reset(void *opaque) +{ + ppc4xx_cpr_t *cpr = opaque; + + cpr->addr = 0; +} + +void ppc4xx_cpr_init(CPUPPCState *env) +{ + ppc4xx_cpr_t *cpr; + + cpr = g_malloc0(sizeof(*cpr)); + ppc_dcr_register(env, CPR0_CFGADDR, cpr, &dcr_read_cpr, &dcr_write_cpr); + ppc_dcr_register(env, CPR0_CFGDATA, cpr, &dcr_read_cpr, &dcr_write_cpr); + qemu_register_reset(ppc4xx_cpr_reset, cpr); +} + +/*****************************************************************************/ +/* System DCRs */ +typedef struct ppc4xx_sdr_t ppc4xx_sdr_t; +struct ppc4xx_sdr_t { + uint32_t addr; +}; + +enum { + SDR0_CFGADDR = 0x00e, + SDR0_CFGDATA, + SDR0_STRP0 = 0x020, + SDR0_STRP1, + SDR0_102 = 0x66, + SDR0_103, + SDR0_128 = 0x80, + SDR0_ECID3 = 0x083, + SDR0_DDR0 = 0x0e1, + SDR0_USB0 = 0x320, +}; + +enum { + PESDR0_LOOP = 0x303, + PESDR0_RCSSET, + PESDR0_RCSSTS, + PESDR0_RSTSTA = 0x310, + PESDR1_LOOP = 0x343, + PESDR1_RCSSET, + PESDR1_RCSSTS, + PESDR1_RSTSTA = 0x365, +}; + +#define SDR0_DDR0_DDRM_ENCODE(n) ((((unsigned long)(n)) & 0x03) << 29) +#define SDR0_DDR0_DDRM_DDR1 0x20000000 +#define SDR0_DDR0_DDRM_DDR2 0x40000000 + +static uint32_t dcr_read_sdr(void *opaque, int dcrn) +{ + ppc4xx_sdr_t *sdr = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case SDR0_CFGADDR: + ret = sdr->addr; + break; + case SDR0_CFGDATA: + switch (sdr->addr) { + case SDR0_STRP0: + /* FIXME: Is this correct? This breaks timing in U-Boot */ + ret = 0; /*(0xb5 << 8) | (1 << 4) | 9 */ + break; + case SDR0_STRP1: + ret = (5 << 29) | (2 << 26) | (1 << 24); + break; + case SDR0_ECID3: + ret = 1 << 20; /* No Security/Kasumi support */ + break; + case SDR0_DDR0: + ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1; + break; + case PESDR0_RCSSET: + case PESDR1_RCSSET: + ret = (1 << 24) | (1 << 16); + break; + case PESDR0_RCSSTS: + case PESDR1_RCSSTS: + ret = (1 << 16) | (1 << 12); + break; + case PESDR0_RSTSTA: + case PESDR1_RSTSTA: + ret = 1; + break; + case PESDR0_LOOP: + case PESDR1_LOOP: + ret = 1 << 12; + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +static void dcr_write_sdr(void *opaque, int dcrn, uint32_t val) +{ + ppc4xx_sdr_t *sdr = opaque; + + switch (dcrn) { + case SDR0_CFGADDR: + sdr->addr = val; + break; + case SDR0_CFGDATA: + switch (sdr->addr) { + case 0x00: /* B0CR */ + break; + default: + break; + } + break; + default: + break; + } +} + +static void sdr_reset(void *opaque) +{ + ppc4xx_sdr_t *sdr = opaque; + + sdr->addr = 0; +} + +void ppc4xx_sdr_init(CPUPPCState *env) +{ + ppc4xx_sdr_t *sdr; + + sdr = g_malloc0(sizeof(*sdr)); + qemu_register_reset(&sdr_reset, sdr); + ppc_dcr_register(env, SDR0_CFGADDR, + sdr, &dcr_read_sdr, &dcr_write_sdr); + ppc_dcr_register(env, SDR0_CFGDATA, + sdr, &dcr_read_sdr, &dcr_write_sdr); + ppc_dcr_register(env, SDR0_102, + sdr, &dcr_read_sdr, &dcr_write_sdr); + ppc_dcr_register(env, SDR0_103, + sdr, &dcr_read_sdr, &dcr_write_sdr); + ppc_dcr_register(env, SDR0_128, + sdr, &dcr_read_sdr, &dcr_write_sdr); + ppc_dcr_register(env, SDR0_USB0, + sdr, &dcr_read_sdr, &dcr_write_sdr); +} + +/*****************************************************************************/ +/* SDRAM controller */ +typedef struct ppc4xx_sdram_t { + uint32_t addr; + int nbanks; + MemoryRegion containers[4]; /* used for clipping */ + MemoryRegion *ram_memories; + hwaddr ram_bases[4]; + hwaddr ram_sizes[4]; + uint32_t bcr[4]; +} ppc4xx_sdram_t; + +enum { + SDRAM0_CFGADDR = 0x10, + SDRAM0_CFGDATA, + SDRAM_R0BAS = 0x40, + SDRAM_R1BAS, + SDRAM_R2BAS, + SDRAM_R3BAS, + SDRAM_CONF1HB = 0x45, + SDRAM_PLBADDULL = 0x4a, + SDRAM_CONF1LL = 0x4b, + SDRAM_CONFPATHB = 0x4f, + SDRAM_PLBADDUHB = 0x50, +}; + +/* XXX: TOFIX: some patches have made this code become inconsistent: + * there are type inconsistencies, mixing hwaddr, target_ulong + * and uint32_t + */ +static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) +{ + uint32_t bcr; + + switch (ram_size) { + case (8 * M_BYTE): + bcr = 0xffc0; + break; + case (16 * M_BYTE): + bcr = 0xff80; + break; + case (32 * M_BYTE): + bcr = 0xff00; + break; + case (64 * M_BYTE): + bcr = 0xfe00; + break; + case (128 * M_BYTE): + bcr = 0xfc00; + break; + case (256 * M_BYTE): + bcr = 0xf800; + break; + case (512 * M_BYTE): + bcr = 0xf000; + break; + case (1 * G_BYTE): + bcr = 0xe000; + break; + default: + error_report("invalid RAM size " TARGET_FMT_plx, ram_size); + return 0; + } + bcr |= ram_base & 0xFF800000; + bcr |= 1; + + return bcr; +} + +static inline hwaddr sdram_base(uint32_t bcr) +{ + return bcr & 0xFF800000; +} + +static target_ulong sdram_size(uint32_t bcr) +{ + target_ulong size; + int sh; + + sh = 1024 - ((bcr >> 6) & 0x3ff); + if (sh == 0) { + size = -1; + } else { + size = 8 * M_BYTE * sh; + } + + return size; +} + +static void sdram_set_bcr(ppc4xx_sdram_t *sdram, + uint32_t *bcrp, uint32_t bcr, int enabled) +{ + unsigned n = bcrp - sdram->bcr; + + if (*bcrp & 1) { + /* Unmap RAM */ + memory_region_del_subregion(get_system_memory(), + &sdram->containers[n]); + memory_region_del_subregion(&sdram->containers[n], + &sdram->ram_memories[n]); + object_unparent(OBJECT(&sdram->containers[n])); + } + *bcrp = bcr & 0xFFDEE001; + if (enabled && (bcr & 1)) { + memory_region_init(&sdram->containers[n], NULL, "sdram-containers", + sdram_size(bcr)); + memory_region_add_subregion(&sdram->containers[n], 0, + &sdram->ram_memories[n]); + memory_region_add_subregion(get_system_memory(), + sdram_base(bcr), + &sdram->containers[n]); + } +} + +static void sdram_map_bcr(ppc4xx_sdram_t *sdram) +{ + int i; + + for (i = 0; i < sdram->nbanks; i++) { + if (sdram->ram_sizes[i] != 0) { + sdram_set_bcr(sdram, + &sdram->bcr[i], + sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), + 1); + } else { + sdram_set_bcr(sdram, &sdram->bcr[i], 0, 0); + } + } +} + +static uint32_t dcr_read_sdram(void *opaque, int dcrn) +{ + ppc4xx_sdram_t *sdram = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case SDRAM_R0BAS: + case SDRAM_R1BAS: + case SDRAM_R2BAS: + case SDRAM_R3BAS: + ret = sdram_bcr(sdram->ram_bases[dcrn - SDRAM_R0BAS], + sdram->ram_sizes[dcrn - SDRAM_R0BAS]); + break; + case SDRAM_CONF1HB: + case SDRAM_CONF1LL: + case SDRAM_CONFPATHB: + case SDRAM_PLBADDULL: + case SDRAM_PLBADDUHB: + break; + case SDRAM0_CFGADDR: + ret = sdram->addr; + break; + case SDRAM0_CFGDATA: + switch (sdram->addr) { + case 0x14: /* SDRAM_MCSTAT (405EX) */ + case 0x1F: + ret = 0x80000000; + break; + case 0x21: /* SDRAM_MCOPT2 */ + ret = 0x08000000; + break; + case 0x40: /* SDRAM_MB0CF */ + ret = 0x00008001; + break; + case 0x7A: /* SDRAM_DLCR */ + ret = 0x02000000; + break; + case 0xE1: /* SDR0_DDR0 */ + ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1; + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +static void dcr_write_sdram(void *opaque, int dcrn, uint32_t val) +{ + ppc4xx_sdram_t *sdram = opaque; + + switch (dcrn) { + case SDRAM_R0BAS: + case SDRAM_R1BAS: + case SDRAM_R2BAS: + case SDRAM_R3BAS: + case SDRAM_CONF1HB: + case SDRAM_CONF1LL: + case SDRAM_CONFPATHB: + case SDRAM_PLBADDULL: + case SDRAM_PLBADDUHB: + break; + case SDRAM0_CFGADDR: + sdram->addr = val; + break; + case SDRAM0_CFGDATA: + switch (sdram->addr) { + case 0x00: /* B0CR */ + break; + default: + break; + } + break; + default: + break; + } +} + +static void sdram_reset(void *opaque) +{ + ppc4xx_sdram_t *sdram = opaque; + + sdram->addr = 0; +} + +void ppc440_sdram_init(CPUPPCState *env, int nbanks, + MemoryRegion *ram_memories, + hwaddr *ram_bases, hwaddr *ram_sizes, + int do_init) +{ + ppc4xx_sdram_t *sdram; + + sdram = g_malloc0(sizeof(*sdram)); + sdram->nbanks = nbanks; + sdram->ram_memories = ram_memories; + memcpy(sdram->ram_bases, ram_bases, nbanks * sizeof(hwaddr)); + memcpy(sdram->ram_sizes, ram_sizes, nbanks * sizeof(hwaddr)); + qemu_register_reset(&sdram_reset, sdram); + ppc_dcr_register(env, SDRAM0_CFGADDR, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM0_CFGDATA, + sdram, &dcr_read_sdram, &dcr_write_sdram); + if (do_init) { + sdram_map_bcr(sdram); + } + + ppc_dcr_register(env, SDRAM_R0BAS, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_R1BAS, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_R2BAS, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_R3BAS, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_CONF1HB, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_PLBADDULL, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_CONF1LL, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_CONFPATHB, + sdram, &dcr_read_sdram, &dcr_write_sdram); + ppc_dcr_register(env, SDRAM_PLBADDUHB, + sdram, &dcr_read_sdram, &dcr_write_sdram); +} + +/*****************************************************************************/ +/* PLB to AHB bridge */ +enum { + AHB_TOP = 0xA4, + AHB_BOT = 0xA5, +}; + +typedef struct ppc4xx_ahb_t { + uint32_t top; + uint32_t bot; +} ppc4xx_ahb_t; + +static uint32_t dcr_read_ahb(void *opaque, int dcrn) +{ + ppc4xx_ahb_t *ahb = opaque; + uint32_t ret = 0; + + switch (dcrn) { + case AHB_TOP: + ret = ahb->top; + break; + case AHB_BOT: + ret = ahb->bot; + break; + default: + break; + } + + return ret; +} + +static void dcr_write_ahb(void *opaque, int dcrn, uint32_t val) +{ + ppc4xx_ahb_t *ahb = opaque; + + switch (dcrn) { + case AHB_TOP: + ahb->top = val; + break; + case AHB_BOT: + ahb->bot = val; + break; + } +} + +static void ppc4xx_ahb_reset(void *opaque) +{ + ppc4xx_ahb_t *ahb = opaque; + + /* No error */ + ahb->top = 0; + ahb->bot = 0; +} + +void ppc4xx_ahb_init(CPUPPCState *env) +{ + ppc4xx_ahb_t *ahb; + + ahb = g_malloc0(sizeof(*ahb)); + ppc_dcr_register(env, AHB_TOP, ahb, &dcr_read_ahb, &dcr_write_ahb); + ppc_dcr_register(env, AHB_BOT, ahb, &dcr_read_ahb, &dcr_write_ahb); + qemu_register_reset(ppc4xx_ahb_reset, ahb); +} + +/*****************************************************************************/ +/* PCI Express controller */ +/* FIXME: This is not complete and does not work, only implemented partially + * to allow firmware and guests to find an empty bus. Cards should use PCI. + */ +#include "hw/pci/pcie_host.h" + +#define TYPE_PPC460EX_PCIE_HOST "ppc460ex-pcie-host" +#define PPC460EX_PCIE_HOST(obj) \ + OBJECT_CHECK(PPC460EXPCIEState, (obj), TYPE_PPC460EX_PCIE_HOST) + +typedef struct PPC460EXPCIEState { + PCIExpressHost host; + + MemoryRegion iomem; + qemu_irq irq[4]; + int32_t dcrn_base; + + uint64_t cfg_base; + uint32_t cfg_mask; + uint64_t msg_base; + uint32_t msg_mask; + uint64_t omr1_base; + uint64_t omr1_mask; + uint64_t omr2_base; + uint64_t omr2_mask; + uint64_t omr3_base; + uint64_t omr3_mask; + uint64_t reg_base; + uint32_t reg_mask; + uint32_t special; + uint32_t cfg; +} PPC460EXPCIEState; + +#define DCRN_PCIE0_BASE 0x100 +#define DCRN_PCIE1_BASE 0x120 + +enum { + PEGPL_CFGBAH = 0x0, + PEGPL_CFGBAL, + PEGPL_CFGMSK, + PEGPL_MSGBAH, + PEGPL_MSGBAL, + PEGPL_MSGMSK, + PEGPL_OMR1BAH, + PEGPL_OMR1BAL, + PEGPL_OMR1MSKH, + PEGPL_OMR1MSKL, + PEGPL_OMR2BAH, + PEGPL_OMR2BAL, + PEGPL_OMR2MSKH, + PEGPL_OMR2MSKL, + PEGPL_OMR3BAH, + PEGPL_OMR3BAL, + PEGPL_OMR3MSKH, + PEGPL_OMR3MSKL, + PEGPL_REGBAH, + PEGPL_REGBAL, + PEGPL_REGMSK, + PEGPL_SPECIAL, + PEGPL_CFG, +}; + +static uint32_t dcr_read_pcie(void *opaque, int dcrn) +{ + PPC460EXPCIEState *state = opaque; + uint32_t ret = 0; + + switch (dcrn - state->dcrn_base) { + case PEGPL_CFGBAH: + ret = state->cfg_base >> 32; + break; + case PEGPL_CFGBAL: + ret = state->cfg_base; + break; + case PEGPL_CFGMSK: + ret = state->cfg_mask; + break; + case PEGPL_MSGBAH: + ret = state->msg_base >> 32; + break; + case PEGPL_MSGBAL: + ret = state->msg_base; + break; + case PEGPL_MSGMSK: + ret = state->msg_mask; + break; + case PEGPL_OMR1BAH: + ret = state->omr1_base >> 32; + break; + case PEGPL_OMR1BAL: + ret = state->omr1_base; + break; + case PEGPL_OMR1MSKH: + ret = state->omr1_mask >> 32; + break; + case PEGPL_OMR1MSKL: + ret = state->omr1_mask; + break; + case PEGPL_OMR2BAH: + ret = state->omr2_base >> 32; + break; + case PEGPL_OMR2BAL: + ret = state->omr2_base; + break; + case PEGPL_OMR2MSKH: + ret = state->omr2_mask >> 32; + break; + case PEGPL_OMR2MSKL: + ret = state->omr3_mask; + break; + case PEGPL_OMR3BAH: + ret = state->omr3_base >> 32; + break; + case PEGPL_OMR3BAL: + ret = state->omr3_base; + break; + case PEGPL_OMR3MSKH: + ret = state->omr3_mask >> 32; + break; + case PEGPL_OMR3MSKL: + ret = state->omr3_mask; + break; + case PEGPL_REGBAH: + ret = state->reg_base >> 32; + break; + case PEGPL_REGBAL: + ret = state->reg_base; + break; + case PEGPL_REGMSK: + ret = state->reg_mask; + break; + case PEGPL_SPECIAL: + ret = state->special; + break; + case PEGPL_CFG: + ret = state->cfg; + break; + } + + return ret; +} + +static void dcr_write_pcie(void *opaque, int dcrn, uint32_t val) +{ + PPC460EXPCIEState *s = opaque; + uint64_t size; + + switch (dcrn - s->dcrn_base) { + case PEGPL_CFGBAH: + s->cfg_base = ((uint64_t)val << 32) | (s->cfg_base & 0xffffffff); + break; + case PEGPL_CFGBAL: + s->cfg_base = (s->cfg_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_CFGMSK: + s->cfg_mask = val; + size = ~(val & 0xfffffffe) + 1; + qemu_mutex_lock_iothread(); + pcie_host_mmcfg_update(PCIE_HOST_BRIDGE(s), val & 1, s->cfg_base, size); + qemu_mutex_unlock_iothread(); + break; + case PEGPL_MSGBAH: + s->msg_base = ((uint64_t)val << 32) | (s->msg_base & 0xffffffff); + break; + case PEGPL_MSGBAL: + s->msg_base = (s->msg_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_MSGMSK: + s->msg_mask = val; + break; + case PEGPL_OMR1BAH: + s->omr1_base = ((uint64_t)val << 32) | (s->omr1_base & 0xffffffff); + break; + case PEGPL_OMR1BAL: + s->omr1_base = (s->omr1_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_OMR1MSKH: + s->omr1_mask = ((uint64_t)val << 32) | (s->omr1_mask & 0xffffffff); + break; + case PEGPL_OMR1MSKL: + s->omr1_mask = (s->omr1_mask & 0xffffffff00000000ULL) | val; + break; + case PEGPL_OMR2BAH: + s->omr2_base = ((uint64_t)val << 32) | (s->omr2_base & 0xffffffff); + break; + case PEGPL_OMR2BAL: + s->omr2_base = (s->omr2_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_OMR2MSKH: + s->omr2_mask = ((uint64_t)val << 32) | (s->omr2_mask & 0xffffffff); + break; + case PEGPL_OMR2MSKL: + s->omr2_mask = (s->omr2_mask & 0xffffffff00000000ULL) | val; + break; + case PEGPL_OMR3BAH: + s->omr3_base = ((uint64_t)val << 32) | (s->omr3_base & 0xffffffff); + break; + case PEGPL_OMR3BAL: + s->omr3_base = (s->omr3_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_OMR3MSKH: + s->omr3_mask = ((uint64_t)val << 32) | (s->omr3_mask & 0xffffffff); + break; + case PEGPL_OMR3MSKL: + s->omr3_mask = (s->omr3_mask & 0xffffffff00000000ULL) | val; + break; + case PEGPL_REGBAH: + s->reg_base = ((uint64_t)val << 32) | (s->reg_base & 0xffffffff); + break; + case PEGPL_REGBAL: + s->reg_base = (s->reg_base & 0xffffffff00000000ULL) | val; + break; + case PEGPL_REGMSK: + s->reg_mask = val; + /* FIXME: how is size encoded? */ + size = (val == 0x7001 ? 4096 : ~(val & 0xfffffffe) + 1); + break; + case PEGPL_SPECIAL: + s->special = val; + break; + case PEGPL_CFG: + s->cfg = val; + break; + } +} + +static void ppc460ex_set_irq(void *opaque, int irq_num, int level) +{ + PPC460EXPCIEState *s = opaque; + qemu_set_irq(s->irq[irq_num], level); +} + +static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) +{ + PPC460EXPCIEState *s = PPC460EX_PCIE_HOST(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + int i, id; + char buf[16]; + + switch (s->dcrn_base) { + case DCRN_PCIE0_BASE: + id = 0; + break; + case DCRN_PCIE1_BASE: + id = 1; + break; + } + snprintf(buf, sizeof(buf), "pcie%d-io", id); + memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX); + for (i = 0; i < 4; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); + } + snprintf(buf, sizeof(buf), "pcie.%d", id); + pci->bus = pci_register_root_bus(DEVICE(s), buf, ppc460ex_set_irq, + pci_swizzle_map_irq_fn, s, &s->iomem, + get_system_io(), 0, 4, TYPE_PCIE_BUS); +} + +static Property ppc460ex_pcie_props[] = { + DEFINE_PROP_INT32("dcrn-base", PPC460EXPCIEState, dcrn_base, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->realize = ppc460ex_pcie_realize; + dc->props = ppc460ex_pcie_props; + dc->hotpluggable = false; +} + +static const TypeInfo ppc460ex_pcie_host_info = { + .name = TYPE_PPC460EX_PCIE_HOST, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(PPC460EXPCIEState), + .class_init = ppc460ex_pcie_class_init, +}; + +static void ppc460ex_pcie_register(void) +{ + type_register_static(&ppc460ex_pcie_host_info); +} + +type_init(ppc460ex_pcie_register) + +static void ppc460ex_pcie_register_dcrs(PPC460EXPCIEState *s, CPUPPCState *env) +{ + ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGBAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_CFGMSK, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGBAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_MSGMSK, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1BAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR1MSKL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2BAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR2MSKL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3BAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_OMR3MSKL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAH, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_REGBAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_REGMSK, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_SPECIAL, s, + &dcr_read_pcie, &dcr_write_pcie); + ppc_dcr_register(env, s->dcrn_base + PEGPL_CFG, s, + &dcr_read_pcie, &dcr_write_pcie); +} + +void ppc460ex_pcie_init(CPUPPCState *env) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_PPC460EX_PCIE_HOST); + qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE0_BASE); + qdev_init_nofail(dev); + object_property_set_bool(OBJECT(dev), true, "realized", NULL); + ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env); + + dev = qdev_create(NULL, TYPE_PPC460EX_PCIE_HOST); + qdev_prop_set_int32(dev, "dcrn-base", DCRN_PCIE1_BASE); + qdev_init_nofail(dev); + object_property_set_bool(OBJECT(dev), true, "realized", NULL); + ppc460ex_pcie_register_dcrs(PPC460EX_PCIE_HOST(dev), env); +} -- cgit v1.1