From ba3c64fb476d57c35013970ac444f04f35893ca9 Mon Sep 17 00:00:00 2001 From: bellard Date: Mon, 5 Dec 2005 20:31:52 +0000 Subject: [PATCH] Initial SPARC SMP support (Blue Swirl) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1694 c046a42c-6fe2-441c-8c8c-71466251a162 --- Changelog | 1 + cpu-all.h | 1 + cpu-exec.c | 14 +++++++- hw/slavio_intctl.c | 83 ++++++++++++++++++++++++++++++++++++++++------ hw/slavio_misc.c | 5 ++- hw/slavio_serial.c | 72 +++++++++++++++++++++++++++++++--------- hw/slavio_timer.c | 25 +++++--------- hw/sun4m.c | 32 ++++++++++++++---- monitor.c | 4 +++ target-sparc/cpu.h | 1 + vl.c | 7 +++- vl.h | 5 ++- 12 files changed, 196 insertions(+), 54 deletions(-) diff --git a/Changelog b/Changelog index a96a5a9dcc..737f3ede28 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version 0.7.3: - MIPS User Linux emulation - MIPS fixes to boot Linux (Daniel Jacobowitz) - NX bit support + - Initial SPARC SMP support (Blue Swirl) version 0.7.2: diff --git a/cpu-all.h b/cpu-all.h index d2086d56b1..b374fe86b0 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -734,6 +734,7 @@ extern int code_copy_enabled; #define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */ #define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */ #define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ +#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ void cpu_interrupt(CPUState *s, int mask); void cpu_reset_interrupt(CPUState *env, int mask); diff --git a/cpu-exec.c b/cpu-exec.c index 128b5ff7a8..740037e4bb 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -274,6 +274,15 @@ int cpu_exec(CPUState *env1) return EXCP_HALTED; } } +#elif defined(TARGET_SPARC) + if (env1->halted) { + if ((env1->interrupt_request & CPU_INTERRUPT_HARD) && + (env1->psret != 0)) { + env1->halted = 0; + } else { + return EXCP_HALTED; + } + } #elif defined(TARGET_ARM) if (env1->halted) { /* An interrupt wakes the CPU even if the I and F CPSR bits are @@ -522,7 +531,10 @@ int cpu_exec(CPUState *env1) } else if (interrupt_request & CPU_INTERRUPT_TIMER) { //do_interrupt(0, 0, 0, 0, 0); env->interrupt_request &= ~CPU_INTERRUPT_TIMER; - } + } else if (interrupt_request & CPU_INTERRUPT_HALT) { + env1->halted = 1; + return EXCP_HALTED; + } #elif defined(TARGET_ARM) if (interrupt_request & CPU_INTERRUPT_FIQ && !(env->uncached_cpsr & CPSR_F)) { diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c index 9780785a47..e43151fad8 100644 --- a/hw/slavio_intctl.c +++ b/hw/slavio_intctl.c @@ -53,6 +53,7 @@ typedef struct SLAVIO_INTCTLState { #ifdef DEBUG_IRQ_COUNT uint64_t irq_count[32]; #endif + CPUState *cpu_envs[MAX_CPUS]; } SLAVIO_INTCTLState; #define INTCTL_MAXADDR 0xf @@ -96,6 +97,7 @@ static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, uint case 2: // set softint val &= 0xfffe0000; s->intreg_pending[cpu] |= val; + slavio_check_interrupts(s); DPRINTF("Set cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]); break; default: @@ -216,7 +218,7 @@ static void slavio_check_interrupts(void *opaque) CPUState *env; SLAVIO_INTCTLState *s = opaque; uint32_t pending = s->intregm_pending; - unsigned int i, max = 0; + unsigned int i, j, max = 0; pending &= ~s->intregm_disabled; @@ -227,20 +229,52 @@ static void slavio_check_interrupts(void *opaque) max = intbit_to_level[i]; } } - env = first_cpu; - if (env->interrupt_index == 0) { - DPRINTF("Triggered pil %d\n", max); + env = s->cpu_envs[s->target_cpu]; + if (!env) { + DPRINTF("No CPU %d, not triggered (pending %x)\n", s->target_cpu, pending); + } + else { + if (env->halted) + env->halted = 0; + if (env->interrupt_index == 0) { + DPRINTF("Triggered CPU %d pil %d\n", s->target_cpu, max); #ifdef DEBUG_IRQ_COUNT - s->irq_count[max]++; + s->irq_count[max]++; #endif - env->interrupt_index = TT_EXTINT | max; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + env->interrupt_index = TT_EXTINT | max; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + else + DPRINTF("Not triggered (pending %x), pending exception %x\n", pending, env->interrupt_index); } - else - DPRINTF("Not triggered (pending %x), pending exception %x\n", pending, env->interrupt_index); } else DPRINTF("Not triggered (pending %x), disabled %x\n", pending, s->intregm_disabled); + + for (i = 0; i < MAX_CPUS; i++) { + max = 0; + env = s->cpu_envs[i]; + if (!env) + continue; + for (j = 17; j < 32; j++) { + if (s->intreg_pending[i] & (1 << j)) { + if (max < j - 16) + max = j - 16; + } + } + if (max > 0) { + if (env->halted) + env->halted = 0; + if (env->interrupt_index == 0) { + DPRINTF("Triggered softint %d for cpu %d (pending %x)\n", max, i, pending); +#ifdef DEBUG_IRQ_COUNT + s->irq_count[max]++; +#endif + env->interrupt_index = TT_EXTINT | max; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + } + } } /* @@ -251,7 +285,7 @@ void slavio_pic_set_irq(void *opaque, int irq, int level) { SLAVIO_INTCTLState *s = opaque; - DPRINTF("Set irq %d level %d\n", irq, level); + DPRINTF("Set cpu %d irq %d level %d\n", s->target_cpu, irq, level); if (irq < 32) { uint32_t mask = 1 << irq; uint32_t pil = intbit_to_level[irq]; @@ -269,6 +303,29 @@ void slavio_pic_set_irq(void *opaque, int irq, int level) slavio_check_interrupts(s); } +void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu) +{ + SLAVIO_INTCTLState *s = opaque; + + DPRINTF("Set cpu %d local irq %d level %d\n", cpu, irq, level); + if (cpu == (unsigned int)-1) { + slavio_pic_set_irq(opaque, irq, level); + return; + } + if (irq < 32) { + uint32_t pil = intbit_to_level[irq]; + if (pil > 0) { + if (level) { + s->intreg_pending[cpu] |= 1 << pil; + } + else { + s->intreg_pending[cpu] &= ~(1 << pil); + } + } + } + slavio_check_interrupts(s); +} + static void slavio_intctl_save(QEMUFile *f, void *opaque) { SLAVIO_INTCTLState *s = opaque; @@ -312,6 +369,12 @@ static void slavio_intctl_reset(void *opaque) s->target_cpu = 0; } +void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env) +{ + SLAVIO_INTCTLState *s = opaque; + s->cpu_envs[cpu] = env; +} + void *slavio_intctl_init(uint32_t addr, uint32_t addrg) { int slavio_intctl_io_memory, slavio_intctlm_io_memory, i; diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c index 1b681be483..904f44e515 100644 --- a/hw/slavio_misc.c +++ b/hw/slavio_misc.c @@ -124,9 +124,8 @@ static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32 case 0xa000000: MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); #if 0 - // XXX: halting CPU does not work - raise_exception(EXCP_HLT); - cpu_loop_exit(); + // XXX almost works + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); #endif break; } diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c index de45cc5de1..2b89c6d62a 100644 --- a/hw/slavio_serial.c +++ b/hw/slavio_serial.c @@ -45,6 +45,8 @@ #ifdef DEBUG_SERIAL #define SER_DPRINTF(fmt, args...) \ do { printf("SER: " fmt , ##args); } while (0) +#define pic_set_irq(irq, level) \ +do { printf("SER: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0) #else #define SER_DPRINTF(fmt, args...) #endif @@ -174,6 +176,50 @@ static void slavio_serial_reset(void *opaque) slavio_serial_reset_chn(&s->chn[1]); } +static inline void clr_rxint(ChannelState *s) +{ + s->rxint = 0; + if (s->chn == 0) + s->rregs[3] &= ~0x20; + else { + s->otherchn->rregs[3] &= ~4; + } + slavio_serial_update_irq(s); +} + +static inline void set_rxint(ChannelState *s) +{ + s->rxint = 1; + if (s->chn == 0) + s->rregs[3] |= 0x20; + else { + s->otherchn->rregs[3] |= 4; + } + slavio_serial_update_irq(s); +} + +static inline void clr_txint(ChannelState *s) +{ + s->txint = 0; + if (s->chn == 0) + s->rregs[3] &= ~0x10; + else { + s->otherchn->rregs[3] &= ~2; + } + slavio_serial_update_irq(s); +} + +static inline void set_txint(ChannelState *s) +{ + s->txint = 1; + if (s->chn == 0) + s->rregs[3] |= 0x10; + else { + s->otherchn->rregs[3] |= 2; + } + slavio_serial_update_irq(s); +} + static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { SerialState *ser = opaque; @@ -198,10 +244,14 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint newreg |= 0x8; break; case 0x20: - s->rxint = 0; + clr_rxint(s); break; case 0x28: - s->txint = 0; + clr_txint(s); + break; + case 0x38: + clr_rxint(s); + clr_txint(s); break; default: break; @@ -247,12 +297,7 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint s->txint = 1; s->rregs[0] |= 4; // Tx buffer empty s->rregs[1] |= 1; // All sent - // Interrupts reported only on channel A - if (s->chn == 0) - s->rregs[3] |= 0x10; - else { - s->otherchn->rregs[3] |= 2; - } + set_txint(s); slavio_serial_update_irq(s); } break; @@ -280,6 +325,7 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) return ret; case 1: s->rregs[0] &= ~1; + clr_rxint(s); if (s->type == kbd) ret = get_queue(s); else @@ -304,16 +350,10 @@ static int serial_can_receive(void *opaque) static void serial_receive_byte(ChannelState *s, int ch) { + SER_DPRINTF("put ch %d\n", ch); s->rregs[0] |= 1; - // Interrupts reported only on channel A - if (s->chn == 0) - s->rregs[3] |= 0x20; - else { - s->otherchn->rregs[3] |= 4; - } s->rx = ch; - s->rxint = 1; - slavio_serial_update_irq(s); + set_rxint(s); } static void serial_receive_break(ChannelState *s) diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c index 47d538529e..d75a76a636 100644 --- a/hw/slavio_timer.c +++ b/hw/slavio_timer.c @@ -42,6 +42,9 @@ do { printf("TIMER: " fmt , ##args); } while (0) * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 * are zero. Bit 31 is 1 when count has been reached. * + * Per-CPU timers interrupt local CPU, system timer uses normal + * interrupt routing. + * */ typedef struct SLAVIO_TIMERState { @@ -53,11 +56,11 @@ typedef struct SLAVIO_TIMERState { int irq; int reached, stopped; int mode; // 0 = processor, 1 = user, 2 = system + unsigned int cpu; } SLAVIO_TIMERState; #define TIMER_MAXADDR 0x1f #define CNT_FREQ 2000000 -#define MAX_CPUS 16 // Update count, set irq, update expire_time static void slavio_timer_get_out(SLAVIO_TIMERState *s) @@ -73,7 +76,7 @@ static void slavio_timer_get_out(SLAVIO_TIMERState *s) else ticks = qemu_get_clock(vm_clock) - s->tick_offset; - out = (ticks >= s->expire_time); + out = (ticks > s->expire_time); if (out) s->reached = 0x80000000; if (!s->limit) @@ -100,7 +103,7 @@ static void slavio_timer_get_out(SLAVIO_TIMERState *s) DPRINTF("irq %d limit %d reached %d d %lld count %d s->c %x diff %lld stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode); if (s->mode != 1) - pic_set_irq(s->irq, out); + pic_set_irq_cpu(s->irq, out, s->cpu); } // timer callback @@ -127,7 +130,7 @@ static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) // part of counter (user mode) if (s->mode != 1) { // clear irq - pic_set_irq(s->irq, 0); + pic_set_irq_cpu(s->irq, 0, s->cpu); s->count_load_time = qemu_get_clock(vm_clock); s->reached = 0; return s->limit; @@ -263,7 +266,7 @@ static void slavio_timer_reset(void *opaque) slavio_timer_get_out(s); } -static void slavio_timer_init_internal(uint32_t addr, int irq, int mode) +void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu) { int slavio_timer_io_memory; SLAVIO_TIMERState *s; @@ -273,6 +276,7 @@ static void slavio_timer_init_internal(uint32_t addr, int irq, int mode) return; s->irq = irq; s->mode = mode; + s->cpu = cpu; s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s); slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read, @@ -282,14 +286,3 @@ static void slavio_timer_init_internal(uint32_t addr, int irq, int mode) qemu_register_reset(slavio_timer_reset, s); slavio_timer_reset(s); } - -void slavio_timer_init(uint32_t addr1, int irq1, uint32_t addr2, int irq2) -{ - int i; - - for (i = 0; i < MAX_CPUS; i++) { - slavio_timer_init_internal(addr1 + i * TARGET_PAGE_SIZE, irq1, 0); - } - - slavio_timer_init_internal(addr2, irq2, 2); -} diff --git a/hw/sun4m.c b/hw/sun4m.c index 3ac0886a40..5733fce5ab 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -56,6 +56,7 @@ #define PHYS_JJ_FDC 0x71400000 /* Floppy */ #define PHYS_JJ_FLOPPY_IRQ 22 #define PHYS_JJ_ME_IRQ 30 /* Module error, power fail */ +#define MAX_CPUS 16 /* TSC handling */ @@ -128,6 +129,8 @@ static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline, nvram_set_string(nvram, 0x00, "QEMU_BIOS", 16); nvram_set_lword(nvram, 0x10, 0x00000001); /* structure v1 */ // NVRAM_size, arch not applicable + m48t59_write(nvram, 0x2D, smp_cpus & 0xff); + m48t59_write(nvram, 0x2E, 0); m48t59_write(nvram, 0x2F, nographic & 0xff); nvram_set_lword(nvram, 0x30, RAM_size); m48t59_write(nvram, 0x34, boot_device & 0xff); @@ -179,6 +182,11 @@ void pic_set_irq(int irq, int level) slavio_pic_set_irq(slavio_intctl, irq, level); } +void pic_set_irq_cpu(int irq, int level, unsigned int cpu) +{ + slavio_pic_set_irq_cpu(slavio_intctl, irq, level, cpu); +} + static void *tcx; void vga_update_display() @@ -222,7 +230,7 @@ static void sun4m_init(int ram_size, int vga_ram_size, int boot_device, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename) { - CPUState *env; + CPUState *env, *envs[MAX_CPUS]; char buf[1024]; int ret, linux_boot; unsigned int i; @@ -230,19 +238,31 @@ static void sun4m_init(int ram_size, int vga_ram_size, int boot_device, linux_boot = (kernel_filename != NULL); - env = cpu_init(); - register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); - qemu_register_reset(main_cpu_reset, env); - + /* init CPUs */ + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(); + envs[i] = env; + if (i != 0) + env->halted = 1; + register_savevm("cpu", i, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); + } /* allocate RAM */ cpu_register_physical_memory(0, ram_size, 0); iommu = iommu_init(PHYS_JJ_IOMMU); slavio_intctl = slavio_intctl_init(PHYS_JJ_INTR0, PHYS_JJ_INTR_G); + for(i = 0; i < smp_cpus; i++) { + slavio_intctl_set_cpu(slavio_intctl, i, envs[i]); + } + tcx = tcx_init(ds, PHYS_JJ_TCX_FB, phys_ram_base + ram_size, ram_size, vram_size, graphic_width, graphic_height); lance_init(&nd_table[0], PHYS_JJ_LE_IRQ, PHYS_JJ_LE, PHYS_JJ_LEDMA); nvram = m48t59_init(0, PHYS_JJ_EEPROM, 0, PHYS_JJ_EEPROM_SIZE, 8); - slavio_timer_init(PHYS_JJ_CLOCK, PHYS_JJ_CLOCK_IRQ, PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ); + for (i = 0; i < MAX_CPUS; i++) { + slavio_timer_init(PHYS_JJ_CLOCK + i * TARGET_PAGE_SIZE, PHYS_JJ_CLOCK_IRQ, 0, i); + } + slavio_timer_init(PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ, 2, (unsigned int)-1); slavio_serial_ms_kbd_init(PHYS_JJ_MS_KBD, PHYS_JJ_MS_KBD_IRQ); // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device diff --git a/monitor.c b/monitor.c index 2f0e84d86e..d7acaa420d 100644 --- a/monitor.c +++ b/monitor.c @@ -259,6 +259,10 @@ static void do_info_cpus(void) term_printf(" nip=0x" TARGET_FMT_lx, env->nip); if (env->halted) term_printf(" (halted)"); +#elif defined(TARGET_SPARC) + term_printf(" pc=0x" TARGET_FMT_lx " npc=0x" TARGET_FMT_lx, env->pc, env->npc); + if (env->halted) + term_printf(" (halted)"); #endif term_printf("\n"); } diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index baff0c4602..28efab758f 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -166,6 +166,7 @@ typedef struct CPUSPARCState { int exception_index; int interrupt_index; int interrupt_request; + int halted; /* NOTE: we allow 8 more registers to handle wrapping */ target_ulong regbase[NWINDOWS * 16 + 8]; diff --git a/vl.c b/vl.c index aa28032b41..4b1e72e9eb 100644 --- a/vl.c +++ b/vl.c @@ -153,6 +153,11 @@ USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; USBDevice *vm_usb_hub; static VLANState *first_vlan; int smp_cpus = 1; +#ifdef TARGET_SPARC +#define MAX_CPUS 16 +#else +#define MAX_CPUS 8 +#endif /***********************************************************/ /* x86 ISA bus support */ @@ -4547,7 +4552,7 @@ int main(int argc, char **argv) break; case QEMU_OPTION_smp: smp_cpus = atoi(optarg); - if (smp_cpus < 1 || smp_cpus > 8) { + if (smp_cpus < 1 || smp_cpus > MAX_CPUS) { fprintf(stderr, "Invalid number of CPUs\n"); exit(1); } diff --git a/vl.h b/vl.h index 2ade7e9e99..fbac807253 100644 --- a/vl.h +++ b/vl.h @@ -806,6 +806,7 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); /* sun4m.c */ extern QEMUMachine sun4m_machine; uint32_t iommu_translate(uint32_t addr); +void pic_set_irq_cpu(int irq, int level, unsigned int cpu); /* iommu.c */ void *iommu_init(uint32_t addr); @@ -823,16 +824,18 @@ void tcx_screen_dump(void *opaque, const char *filename); /* slavio_intctl.c */ void *slavio_intctl_init(); +void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env); void slavio_pic_info(void *opaque); void slavio_irq_info(void *opaque); void slavio_pic_set_irq(void *opaque, int irq, int level); +void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu); /* magic-load.c */ int load_elf(const char *filename, uint8_t *addr); int load_aout(const char *filename, uint8_t *addr); /* slavio_timer.c */ -void slavio_timer_init(uint32_t addr1, int irq1, uint32_t addr2, int irq2); +void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu); /* slavio_serial.c */ SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2);