sparc32: fix per cpu counter/timer
PATCH 1/1 sparc32: fix per cpu counter/timer
Sun4m SMP machines support a maximum of 4 CPUs. Linux knows this and uses fixed size arrays for per-cpu counter/timers and interrupt controllers. Sun4m uni-processor machines use the slaveio chip which has a single per-cpu counter/timer and interrupt controller. However it does not fully decode the address so the same counter/timer or interrupt controller can be accesses from multiple addresses.
This patch changes the per-cpu counter/timer to work the way the real hardware works: 4 per-cpu counter/timers for SMP and 1 counter/timer for UP mapped at multiple addresses.
This patch also fixes a number of per-cpu user timer bugs:
limit bit set when limit reached, count saved and used when written, limit bit reset on count write and system timer configuration register updated properly for per-cpu user timer mode.
Sun4d currently uses the sun4m counter/timer code. They are simular but not the same. This patch will break the broken sun4d implementation further. The real fix is to create a proper sun4d counter/timer implementation. Since the sun4d implementation doesn't currently work anyway, this shouldn't be an issue.
Index: hw/sbi.c
===================================================================
RCS file: /sources/qemu/qemu/hw/sbi.c,v
retrieving revision 1.2
sbi.c +8 -10
@@ -34,15 +34,13 @@ do { printf("IRQ: " fmt , ##args); } whi #define DPRINTF(fmt, args...)#endif -#define MAX_CPUS 16 -#define SBI_NREGS 16typedef struct SBIState {uint32_t regs[SBI_NREGS ]; -uint32_t intreg_pending[MAX_CPUS ]; -qemu_irq *cpu_irqs[MAX_CPUS ]; -uint32_t pil_out[MAX_CPUS ]; +uint32_t intreg_pending[MAX_SUN4D_CPUS ]; +qemu_irq *cpu_irqs[MAX_SUN4D_CPUS ]; +uint32_t pil_out[MAX_SUN4D_CPUS ]; }SBIState ;#define SBI_SIZE (SBI_NREGS * 4)
@@ -107,7 +105,7 @@ static void sbi_save(QEMUFile *f, void *SBIState *s = opaque;unsigned int i; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4D_CPUS ; i++) {qemu_put_be32s (f, &s->intreg_pending[i]); } }
@@ -120,7 +118,7 @@ static int sbi_load(QEMUFile *f, void *oif (version_id !=1 )return -EINVAL ; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4D_CPUS ; i++) {qemu_get_be32s (f, &s->intreg_pending[i]); }sbi_check_interrupts (s);
@@ -133,7 +131,7 @@ static void sbi_reset(void *opaque)SBIState *s = opaque;unsigned int i; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4D_CPUS ; i++) { s->intreg_pending[i] =0 ; }sbi_check_interrupts (s);
@@ -150,7 +148,7 @@ void *sbi_init(target_phys_addr_t addr,if (!s)return NULL ; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4D_CPUS ; i++) { s->cpu_irqs[i] = parent_irq[i]; }
@@ -160,7 +158,7 @@ void *sbi_init(target_phys_addr_t addr,register_savevm ("sbi" , addr,1 , sbi_save, sbi_load, s);qemu_register_reset (sbi_reset, s); *irq =qemu_allocate_irqs (sbi_set_irq, s,32 ); - *cpu_irq =qemu_allocate_irqs (sbi_set_timer_irq_cpu, s,MAX_CPUS ); + *cpu_irq =qemu_allocate_irqs (sbi_set_timer_irq_cpu, s,MAX_SUN4D_CPUS );sbi_reset (s);return s;
Index: hw/slavio_intctl.c
===================================================================
RCS file: /sources/qemu/qemu/hw/slavio_intctl.c,v
retrieving revision 1.29
slavio_intctl.c +15 -16
@@ -46,21 +46,20 @@ do { printf("IRQ: " fmt , ##args); } whi* */ -#define MAX_CPUS 16#define MAX_PILS 16typedef struct SLAVIO_INTCTLState { -uint32_t intreg_pending[MAX_CPUS ]; +uint32_t intreg_pending[MAX_SUN4M_CPUS ];uint32_t intregm_pending;uint32_t intregm_disabled;uint32_t target_cpu;#ifdef DEBUG_IRQ_COUNT uint64_t irq_count[32 ];#endif -qemu_irq *cpu_irqs[MAX_CPUS ]; +qemu_irq *cpu_irqs[MAX_SUN4M_CPUS ];const uint32_t *intbit_to_level;uint32_t cputimer_lbit, cputimer_mbit; -uint32_t pil_out[MAX_CPUS ]; +uint32_t pil_out[MAX_SUN4M_CPUS ]; }SLAVIO_INTCTLState ;#define INTCTL_MAXADDR 0xf
@@ -84,7 +83,7 @@ static uint32_t slavio_intctl_mem_readl(uint32_t saddr, ret;int cpu; - cpu = (addr & (MAX_CPUS -1 ) *TARGET_PAGE_SIZE ) >>12 ; + cpu = (addr & (MAX_SUN4M_CPUS -1 ) *TARGET_PAGE_SIZE ) >>12 ; saddr = (addr &INTCTL_MAXADDR ) >>2 ;switch (saddr) {case 0 :
@@ -105,7 +104,7 @@ static void slavio_intctl_mem_writel(voiuint32_t saddr;int cpu; - cpu = (addr & (MAX_CPUS -1 ) *TARGET_PAGE_SIZE ) >>12 ; + cpu = (addr & (MAX_SUN4M_CPUS -1 ) *TARGET_PAGE_SIZE ) >>12 ; saddr = (addr &INTCTL_MAXADDR ) >>2 ;DPRINTF ("write cpu %d reg 0x" TARGET_FMT_plx" = %x\n" , cpu, addr, val);switch (saddr) {
@@ -190,7 +189,7 @@ static void slavio_intctlm_mem_writel(voDPRINTF ("Disabled master irq mask %x, curmask %x\n" , val, s->intregm_disabled);break ;case 4 : - s->target_cpu = val & (MAX_CPUS -1 ); + s->target_cpu = val & (MAX_SUN4M_CPUS -1 );slavio_check_interrupts (s);DPRINTF ("Set master irq cpu %d\n" , s->target_cpu);break ;
@@ -216,7 +215,7 @@ void slavio_pic_info(void *opaque)SLAVIO_INTCTLState *s = opaque;int i; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) {term_printf ("per-cpu %d: pending 0x%08x\n" , i, s->intreg_pending[i]); }term_printf ("master: pending 0x%08x, disabled 0x%08x\n" , s->intregm_pending, s->intregm_disabled);
@@ -249,7 +248,7 @@ static void slavio_check_interrupts(void pending &= ~s->intregm_disabled;DPRINTF ("pending %x disabled %x\n" , pending, s->intregm_disabled); -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) { pil_pending =0 ;if (pending && !(s->intregm_disabled &MASTER_DISABLE ) && (i == s->target_cpu)) {
@@ -322,7 +321,7 @@ static void slavio_intctl_save(QEMUFileSLAVIO_INTCTLState *s = opaque;int i; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) {qemu_put_be32s (f, &s->intreg_pending[i]); }qemu_put_be32s (f, &s->intregm_pending);
@@ -335,10 +334,10 @@ static int slavio_intctl_load(QEMUFile *SLAVIO_INTCTLState *s = opaque;int i; -if (version_id !=1 ) +if (version_id !=2 )return -EINVAL ; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) {qemu_get_be32s (f, &s->intreg_pending[i]); }qemu_get_be32s (f, &s->intregm_pending);
@@ -353,7 +352,7 @@ static void slavio_intctl_reset(void *opSLAVIO_INTCTLState *s = opaque;int i; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) { s->intreg_pending[i] =0 ; } s->intregm_disabled = ~MASTER_IRQ_MASK ;
@@ -375,7 +374,7 @@ void *slavio_intctl_init(target_phys_addreturn NULL ; s->intbit_to_level = intbit_to_level; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) { slavio_intctl_io_memory =cpu_register_io_memory (0 , slavio_intctl_mem_read, slavio_intctl_mem_write, s);cpu_register_physical_memory (addr + i *TARGET_PAGE_SIZE ,INTCTL_SIZE , slavio_intctl_io_memory);
@@ -385,11 +384,11 @@ void *slavio_intctl_init(target_phys_add slavio_intctlm_io_memory =cpu_register_io_memory (0 , slavio_intctlm_mem_read, slavio_intctlm_mem_write, s);cpu_register_physical_memory (addrg,INTCTLM_SIZE , slavio_intctlm_io_memory); -register_savevm ("slavio_intctl" , addr,1 , slavio_intctl_save, slavio_intctl_load, s); +register_savevm ("slavio_intctl" , addr,2 , slavio_intctl_save, slavio_intctl_load, s);qemu_register_reset (slavio_intctl_reset, s); *irq =qemu_allocate_irqs (slavio_set_irq, s,32 ); - *cpu_irq =qemu_allocate_irqs (slavio_set_timer_irq_cpu, s,MAX_CPUS ); + *cpu_irq =qemu_allocate_irqs (slavio_set_timer_irq_cpu, s,MAX_SUN4M_CPUS ); s->cputimer_mbit =1 << cputimer; s->cputimer_lbit =1 << intbit_to_level[cputimer];slavio_intctl_reset (s);
Index: hw/slavio_timer.c
===================================================================
RCS file: /sources/qemu/qemu/hw/slavio_timer.c,v
retrieving revision 1.28
slavio_timer.c +102 -73
315 lines not on this page · raw message
Index: hw/sun4m.c
===================================================================
RCS file: /sources/qemu/qemu/hw/sun4m.c,v
retrieving revision 1.79
sun4m.c +41 -17
161 lines not on this page · raw message
Index: hw/sun4m.h
===================================================================
RCS file: /sources/qemu/qemu/hw/sun4m.h,v
retrieving revision 1.8
sun4m.h +4 -1
@@ -1,6 +1,9 @@#ifndef SUN4M_H #define SUN4M_H +#define MAX_SUN4M_CPUS 4 +#define MAX_SUN4D_CPUS 20 +/* Devices used by sparc32 system. */ /* iommu.c */
@@ -44,7 +47,7 @@ void *sun4c_intctl_init(target_phys_addr/* slavio_timer.c */ void slavio_timer_init_all (target_phys_addr_t base,qemu_irq master_irq, -qemu_irq *cpu_irqs,unsigned int num_cpus); +qemu_irq *cpu_irqs,int smp);/* slavio_serial.c */ SerialState *slavio_serial_init(target_phys_addr_t base, qemu_irq irq,
Quoted 11 lines · Robert Reif
Sun4m SMP machines support a maximum of 4 CPUs. Linux knows this and uses fixed size arrays for per-cpu counter/timers and interrupt controllers. Sun4m uni-processor machines use the slaveio chip which has a single per-cpu counter/timer and interrupt controller. However it does not fully decode the address so the same counter/timer or interrupt controller can be accesses from multiple addresses.
This patch changes the per-cpu counter/timer to work the way the real hardware works: 4 per-cpu counter/timers for SMP and 1 counter/timer for UP mapped at multiple addresses.
The idea for this part is OK, but I don't like some parts of the implementation, see below.
Quoted 4 lines
This patch also fixes a number of per-cpu user timer bugs:
limit bit set when limit reached, count saved and used when written, limit bit reset on count write and system timer configuration register updated properly for per-cpu user timer mode.
These changes should be in separate patches.
Quoted 5 lines
Sun4d currently uses the sun4m counter/timer code. They are simular but not the same. This patch will break the broken sun4d implementation further. The real fix is to create a proper sun4d counter/timer implementation. Since the sun4d implementation doesn't currently work anyway, this shouldn't be an issue.
Well, why bother then?
slavio_timer.c typedef struct SLAVIO_TIMERState { · line 68
Quoted 4 lines
slavio_timer.c +2
-2-unsigned int num_slaves; -struct SLAVIO_TIMERState *slave[MAX_CPUS ]; +int smp; +struct SLAVIO_TIMERState *slave[MAX_SUN4M_CPUS ];
I don't think the change from num_slaves to smp is needed.
slavio_timer.c static int slavio_timer_is_user(SLAVIO_T · line 102
Quoted 4 lines
slavio_timer.c +4
-0+static int slavio_timer_is_mapped (SLAVIO_TIMERState *s) +{ +return s->master && (!s->master->smp && s->slave_index >1 ); +}
These kind of helpers should be introduced so that the logic is not changed, now it's hard to see what changes and what doesn't.
slavio_timer.c static void slavio_timer_get_out(SLAVIO_ · line 115
Quoted 5 lines
slavio_timer.c +1
-4-if (s->timer) - count = limit -PERIODS_TO_LIMIT (ptimer_get_count (s->timer)); -else - count =0 ; + count = limit -PERIODS_TO_LIMIT (ptimer_get_count (s->timer));
Same here.
slavio_timer.c static uint32_t slavio_timer_mem_readl(v · line 142
Quoted 3 lines
slavio_timer.c +3
-0+if (slavio_timer_is_mapped (s)) + s = s->master->slave[0 ]; +
And here.
Quoted 32 lines
slavio_timer.c +12
-10- s->limit =TIMER_MAX_COUNT64 ; -DPRINTF ("processor %d user timer reset\n" , s->slave_index); -if (s->timer) -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); + s->reached =0 ; + s->counthigh = val & (TIMER_MAX_COUNT64 >>32 ); + count = s->count | (uint64_t )s->counthigh <<32 ; +DPRINTF ("processor %d user timer set to %016llx\n" , + original->slave_index, count); +ptimer_set_count (s->timer,LIMIT_TO_PERIODS (s->limit - count)); }else {// set limit, reset counter qemu_irq_lower (s->irq); s->limit = val &TIMER_MAX_COUNT32 ; -if (s->timer) { -if (s->limit ==0 )/* free-run */ -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (TIMER_MAX_COUNT32 ),1 ); -else -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); - } +if (s->limit ==0 )/* free-run */ +ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (TIMER_MAX_COUNT32 ), +1 ); +else +ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); }break ;case TIMER_COUNTER :if (slavio_timer_is_user (s)) { +uint64_t count;// set user counter LSW, reset counter qemu_irq_lower (s->irq);
slavio_timer.c static void slavio_timer_mem_writel(void · line 236
Quoted 10 lines
slavio_timer.c +6
-4- s->limit =TIMER_MAX_COUNT64 ; -DPRINTF ("processor %d user timer reset\n" , s->slave_index); -if (s->timer) -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); + s->reached =0 ; + s->count = val &TIMER_MAX_COUNT64 ; + count = s->count | (uint64_t )s->counthigh <<32 ; +DPRINTF ("processor %d user timer set to %016llx\n" , + original->slave_index, count); +ptimer_set_count (s->timer,LIMIT_TO_PERIODS (s->limit - count));
These are separate.
Quoted 35 lines
slavio_timer.c +21
-14-for (i =0 ; i < s->num_slaves; i++) { -if (val & (1 << i)) { -qemu_irq_lower (s->slave[i]->irq); - s->slave[i]->limit =-1ULL ; - }else { -ptimer_stop (s->slave[i]->timer); - } -if ((val & (1 << i)) != (s->slave_mode & (1 << i))) { -ptimer_stop (s->slave[i]->timer); -ptimer_set_limit (s->slave[i]->timer, -LIMIT_TO_PERIODS (s->slave[i]->limit),1 ); -DPRINTF ("processor %d timer changed\n" , - s->slave[i]->slave_index); -ptimer_run (s->slave[i]->timer,0 ); +for (i =0 ; i < num_slaves; i++) { +unsigned int processor =1 << i; +// check for a change in timer mode for this processor +if ((val & processor) != (s->slave_mode & processor)) { +if (val & processor) {// counter -> user timer +qemu_irq_lower (s->slave[i]->irq); +// counters are always running +ptimer_stop (s->slave[i]->timer); + s->slave[i]->running =0 ; +// user timer limit is always the same + s->slave[i]->limit =TIMER_MAX_COUNT64 ; +ptimer_set_limit (s->slave[i]->timer, +LIMIT_TO_PERIODS (s->slave[i]->limit),1 ); +// set this processors user timer bit in config +// register + s->slave_mode |= processor; +DPRINTF ("processor %d changed from counter to user " +"timer\n" , s->slave[i]->slave_index); + }else {// user timer -> counter +// stop the user timer if it is running +if (s->slave[i]->running)
slavio_timer.c static void slavio_timer_mem_writel(void · line 296
Quoted 10 lines
slavio_timer.c +10
-0+ptimer_stop (s->slave[i]->timer); +// start the counter +ptimer_run (s->slave[i]->timer,0 ); + s->slave[i]->running =1 ; +// clear this processors user timer bit in config +// register + s->slave_mode &= ~processor; +DPRINTF ("processor %d changed from user timer to " +"counter\n" , s->slave[i]->slave_index); + }
Too many changes at once.
Quoted 45 lines
static
SLAVIO_TIMERState*slavio_timer_init(target_phys_addr_t addr,
qemu_irqirq,SLAVIO_TIMERState*master,slavio_timer.c +10
-8- int slave_index) + int slave_index, int mapped) {int slavio_timer_io_memory;SLAVIO_TIMERState *s;@@ -349,7 +375,7 @@ static SLAVIO_TIMERState *slavio_timer_i s->irq = irq; s->master = master; s->slave_index = slave_index; -if (!master || slave_index < master->num_slaves) { +if (!mapped) {/* don't create a qemu timer for mapped devices */ bh =qemu_bh_new (slavio_timer_irq, s); s->timer =ptimer_init (bh);ptimer_set_period (s->timer,TIMER_PERIOD );@@ -363,27 +389,30 @@ static SLAVIO_TIMERState *slavio_timer_ielse cpu_register_physical_memory (addr,SYS_TIMER_SIZE , slavio_timer_io_memory); -register_savevm ("slavio_timer" , addr,3 , slavio_timer_save, - slavio_timer_load, s); -qemu_register_reset (slavio_timer_reset, s); -slavio_timer_reset (s); +if (!mapped) {/* don't register mapped devices */ +register_savevm ("slavio_timer" , addr,3 , slavio_timer_save, + slavio_timer_load, s); +qemu_register_reset (slavio_timer_reset, s); +slavio_timer_reset (s); + }return s; }void slavio_timer_init_all (target_phys_addr_t base,qemu_irq master_irq, -qemu_irq *cpu_irqs,unsigned int num_cpus) +qemu_irq *cpu_irqs,int smp) {SLAVIO_TIMERState *master;unsigned int i; - master =slavio_timer_init (base +SYS_TIMER_OFFSET , master_irq,NULL ,0 ); + master =slavio_timer_init (base +SYS_TIMER_OFFSET , master_irq,NULL ,0 ,0 );
slavio_timer.c static SLAVIO_TIMERState *slavio_timer_i · line 416
Quoted 10 lines
slavio_timer.c +4
-3- master->num_slaves = num_cpus; + master->smp = smp; -for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) { master->slave[i] =slavio_timer_init (base + (target_phys_addr_t )CPU_TIMER_OFFSET (i), - cpu_irqs[i], master, i); + cpu_irqs[i], master, i, + !smp && i !=0 );
This part is not OK. "mapped" should be "disabled" or something more descriptive. Currently a functioning device is created for units that have a corresponding CPU, and a non-functioning device for the other slots. I think that a non-functioning device is still needed for other slots for SMP kernels to work.
sun4m.c static void sun4m_hw_init(const struct h · line 386
Quoted 6 lines
sun4m.c +6
-0+if ((hwdef->machine_id ==0x80 && smp_cpus >1 ) || + (hwdef->machine_id !=0x80 && smp_cpus >MAX_SUN4M_CPUS )) { +fprintf (stderr, +"qemu: Too many CPUs for this machine: %d maiximum %d\n" , + smp_cpus, hwdef->machine_id ==0x80 ?1 :MAX_SUN4M_CPUS ); +exit (1 );
I don't want to limit the CPUs, so a warning is enough. A cleaner implementation is to put the CPU limit and extra timer parameters to hwdef.
Review
Quoted 2 lines
(quoted diff) +1
-1- fprintf(stderr, "Unable to find Sparc CPU definition\n"); + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
Unrelated, like the next few wrapping changes.
This patch is trying to make qemu behave like real hardware. This is what
the OSs expect. The ability to create hardware that never existed and can't
exist due to real hardware limitations is cool but it's not going to work
properly with existing OSs. At best you will have the OS never accessing
the extra hardware because of known hardwired limits in the OS or worse,
you will have the OS accessing off the end of fixed size arrays. Neither
are fixable without changing the OS. I know that the boot prom provides
a layer of abstraction between the hardware and the OS and linux is very
trusting of what the prom provides, even when it makes no sense. However there are some assumptions that the OS writers knew about and do ignore what the prom says. You can have the prom tell linux that
there a a million CPUs and it will happily print out that the prom said
there are a million CPUs but it knows that there are 4 and has fixed size
arrays for just those 4 :-)
Quoted 9 lines · Blue Swirl
slavio_timer.c typedef struct SLAVIO_TIMERState { · line 68
Quoted 6 lines
slavio_timer.c +2
-2-unsigned int num_slaves; -struct SLAVIO_TIMERState *slave[MAX_CPUS ]; +int smp; +struct SLAVIO_TIMERState *slave[MAX_SUN4M_CPUS ];I don't think the change from
num_slavesto smp is needed.
The number is a constant, 1 for UP and 4 for SMP. That's defined by the real hardware. The OS writers knew that.
Quoted 11 lines
slavio_timer.c static int slavio_timer_is_user(SLAVIO_T · line 102
Quoted 6 lines
slavio_timer.c +4
-0+static int slavio_timer_is_mapped (SLAVIO_TIMERState *s) +{ +return s->master && (!s->master->smp && s->slave_index >1 ); +}These kind of helpers should be introduced so that the logic is not changed, now it's hard to see what changes and what doesn't.
This doesn't change any logic. It's just a helper to determine if the counter/timer being accessed is really the one at that address or another one being accessed from a different address.
Quoted 11 lines
slavio_timer.c static void slavio_timer_get_out(SLAVIO_ · line 115
Quoted 7 lines
slavio_timer.c +1
-4-if (s->timer) - count = limit -PERIODS_TO_LIMIT (ptimer_get_count (s->timer)); -else - count =0 ; + count = limit -PERIODS_TO_LIMIT (ptimer_get_count (s->timer));Same here.
There are never any counter/timers created with s->timer being NULL. We are creating alternate address ranges for the mapped counter/timers so qemu is happy but we redirect access to those spaces to a single real counter/timer. The alternate address spaces are never accessed. Thats why they and not initialized, reset, loaded or saved.
Quoted 9 lines
slavio_timer.c static uint32_t slavio_timer_mem_readl(v · line 142
Quoted 5 lines
slavio_timer.c +3
-0+if (slavio_timer_is_mapped (s)) + s = s->master->slave[0 ]; +And here.
Here we are redirecting accesses to mapped counter/timers to the single real one.
Quoted 97 lines
Quoted 34 lines
slavio_timer.c +12
-10- s->limit =TIMER_MAX_COUNT64 ; -DPRINTF ("processor %d user timer reset\n" , s->slave_index); -if (s->timer) -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); + s->reached =0 ; + s->counthigh = val & (TIMER_MAX_COUNT64 >>32 ); + count = s->count | (uint64_t )s->counthigh <<32 ; +DPRINTF ("processor %d user timer set to %016llx\n" , + original->slave_index, count); +ptimer_set_count (s->timer,LIMIT_TO_PERIODS (s->limit - count)); }else {// set limit, reset counter qemu_irq_lower (s->irq); s->limit = val &TIMER_MAX_COUNT32 ; -if (s->timer) { -if (s->limit ==0 )/* free-run */ -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (TIMER_MAX_COUNT32 ),1 ); -else -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); - } +if (s->limit ==0 )/* free-run */ +ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (TIMER_MAX_COUNT32 ), +1 ); +else +ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); }break ;case TIMER_COUNTER :if (slavio_timer_is_user (s)) { +uint64_t count;// set user counter LSW, reset counter qemu_irq_lower (s->irq);slavio_timer.c static void slavio_timer_mem_writel(void · line 236
Quoted 10 lines
slavio_timer.c +6
-4- s->limit =TIMER_MAX_COUNT64 ; -DPRINTF ("processor %d user timer reset\n" , s->slave_index); -if (s->timer) -ptimer_set_limit (s->timer,LIMIT_TO_PERIODS (s->limit),1 ); + s->reached =0 ; + s->count = val &TIMER_MAX_COUNT64 ; + count = s->count | (uint64_t )s->counthigh <<32 ; +DPRINTF ("processor %d user timer set to %016llx\n" , + original->slave_index, count); +ptimer_set_count (s->timer,LIMIT_TO_PERIODS (s->limit - count));These are separate.
Quoted 37 lines
slavio_timer.c +21
-14-for (i =0 ; i < s->num_slaves; i++) { -if (val & (1 << i)) { -qemu_irq_lower (s->slave[i]->irq); - s->slave[i]->limit =-1ULL ; - }else { -ptimer_stop (s->slave[i]->timer); - } -if ((val & (1 << i)) != (s->slave_mode & (1 << i))) { -ptimer_stop (s->slave[i]->timer); -ptimer_set_limit (s->slave[i]->timer, -LIMIT_TO_PERIODS (s->slave[i]->limit),1 ); -DPRINTF ("processor %d timer changed\n" , - s->slave[i]->slave_index); -ptimer_run (s->slave[i]->timer,0 ); +for (i =0 ; i < num_slaves; i++) { +unsigned int processor =1 << i; +// check for a change in timer mode for this processor +if ((val & processor) != (s->slave_mode & processor)) { +if (val & processor) {// counter -> user timer +qemu_irq_lower (s->slave[i]->irq); +// counters are always running +ptimer_stop (s->slave[i]->timer); + s->slave[i]->running =0 ; +// user timer limit is always the same + s->slave[i]->limit =TIMER_MAX_COUNT64 ; +ptimer_set_limit (s->slave[i]->timer, +LIMIT_TO_PERIODS (s->slave[i]->limit),1 ); +// set this processors user timer bit in config +// register + s->slave_mode |= processor; +DPRINTF ("processor %d changed from counter to user " +"timer\n" , s->slave[i]->slave_index); + }else {// user timer -> counter +// stop the user timer if it is running +if (s->slave[i]->running)slavio_timer.c static void slavio_timer_mem_writel(void · line 296
Quoted 10 lines
slavio_timer.c +10
-0+ptimer_stop (s->slave[i]->timer); +// start the counter +ptimer_run (s->slave[i]->timer,0 ); + s->slave[i]->running =1 ; +// clear this processors user timer bit in config +// register + s->slave_mode &= ~processor; +DPRINTF ("processor %d changed from user timer to " +"counter\n" , s->slave[i]->slave_index); + }
Quoted 3 lines
Too many changes at once.
The original code was really broken and not salvagable. This code only makes changes when something actually changes and makes the proper changes to reflect how the hardware actually works. The code immediately above this also makes sure that that only real hardware is accessed. I could break this up into less stages of brokenness until it's finally fixed but those intermediate patches would still be broken, just less so.
Quoted 48 lines
Quoted 48 lines
static
SLAVIO_TIMERState*slavio_timer_init(target_phys_addr_t addr,
qemu_irqirq,SLAVIO_TIMERState*master,slavio_timer.c +1
-1- int slave_index) + int slave_index, int mapped){ int slavio_timer_io_memory; SLAVIO_TIMERState *s;slavio_timer.c +10
-8@@ -349,7 +375,7 @@ static SLAVIO_TIMERState *slavio_timer_i s->irq = irq; s->master = master; s->slave_index = slave_index; -if (!master || slave_index < master->num_slaves) { +if (!mapped) {/* don't create a qemu timer for mapped devices */ bh =qemu_bh_new (slavio_timer_irq, s); s->timer =ptimer_init (bh);ptimer_set_period (s->timer,TIMER_PERIOD );@@ -363,27 +389,30 @@ static SLAVIO_TIMERState *slavio_timer_ielse cpu_register_physical_memory (addr,SYS_TIMER_SIZE , slavio_timer_io_memory); -register_savevm ("slavio_timer" , addr,3 , slavio_timer_save, - slavio_timer_load, s); -qemu_register_reset (slavio_timer_reset, s); -slavio_timer_reset (s); +if (!mapped) {/* don't register mapped devices */ +register_savevm ("slavio_timer" , addr,3 , slavio_timer_save, + slavio_timer_load, s); +qemu_register_reset (slavio_timer_reset, s); +slavio_timer_reset (s); + }return s; }void slavio_timer_init_all (target_phys_addr_t base,qemu_irq master_irq, -qemu_irq *cpu_irqs,unsigned int num_cpus) +qemu_irq *cpu_irqs,int smp) {SLAVIO_TIMERState *master;unsigned int i; - master =slavio_timer_init (base +SYS_TIMER_OFFSET , master_irq,NULL ,0 ); + master =slavio_timer_init (base +SYS_TIMER_OFFSET , master_irq,NULL ,0 ,0 ); - master->num_slaves = num_cpus; + master->smp = smp;
Quoted 17 lines
slavio_timer.c static SLAVIO_TIMERState *slavio_timer_i
Quoted 9 lines
slavio_timer.c +3
-2-for (i =0 ; i <MAX_CPUS ; i++) { +for (i =0 ; i <MAX_SUN4M_CPUS ; i++) { master->slave[i] =slavio_timer_init (base + (target_phys_addr_t )CPU_TIMER_OFFSET (i), - cpu_irqs[i], master, i); + cpu_irqs[i], master, i, + !smp && i !=0 );This part is not OK. "mapped" should be "disabled" or something more descriptive. Currently a functioning device is created for units that have a corresponding CPU, and a non-functioning device for the other slots. I think that a non-functioning device is still needed for other slots for SMP kernels to work.
The counter/timer is not disabled. It is not there. The memory space is allocated for it in qemu but all accesses are redirected (mapped) to the single real counter/timer.
Quoted 9 lines
sun4m.c static void sun4m_hw_init(const struct h · line 386
Quoted 8 lines
sun4m.c +6
-0+if ((hwdef->machine_id ==0x80 && smp_cpus >1 ) || + (hwdef->machine_id !=0x80 && smp_cpus >MAX_SUN4M_CPUS )) { +fprintf (stderr, +"qemu: Too many CPUs for this machine: %d maiximum %d\n" , + smp_cpus, hwdef->machine_id ==0x80 ?1 :MAX_SUN4M_CPUS ); +exit (1 );
Quoted 5 lines
I don't want to limit the CPUs, so a warning is enough. A cleaner implementation is to put the CPU limit and extra timer parameters to hwdef.
That's the whole point of this patch. There never were any sun4m SMP machines with more than 4 CPUs and there never will be. OS writers know that. That's why linux has fixed size arrays for per-cpu resources. It's a a hardware and architectural limitation for sun4m machines. You would need to patch linux to make more than 4 CPU work. What's the point of doing that. qemu doesn't really support smp anyway. SMP instruction locking isn't implemented in qemu.
Quoted 9 lines
Review
Quoted 4 lines
(quoted diff) +1
-1- fprintf(stderr, "Unable to find Sparc CPU definition\n"); + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");Unrelated, like the next few wrapping changes.
All but a few have qemu: in the error message. I'm just correcting an over site but that can go in a split up patch.