aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2005-12-05 20:31:52 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2005-12-05 20:31:52 +0000
commitba3c64fb476d57c35013970ac444f04f35893ca9 (patch)
treeef1a50165c32cf861862c94cfd9bcf9ebffcd9f2 /hw
parentb9788fc4c4974a4871e0ee46b5c06fee105a6aff (diff)
downloadqemu-ba3c64fb476d57c35013970ac444f04f35893ca9.zip
qemu-ba3c64fb476d57c35013970ac444f04f35893ca9.tar.gz
qemu-ba3c64fb476d57c35013970ac444f04f35893ca9.tar.bz2
Initial SPARC SMP support (Blue Swirl)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1694 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/slavio_intctl.c83
-rw-r--r--hw/slavio_misc.c5
-rw-r--r--hw/slavio_serial.c72
-rw-r--r--hw/slavio_timer.c25
-rw-r--r--hw/sun4m.c32
5 files changed, 166 insertions, 51 deletions
diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c
index 9780785..e43151f 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 1b681be..904f44e 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 de45cc5..2b89c6d 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 47d5385..d75a76a 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 3ac0886..5733fce 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