* [PATCH 0/4] Fix some MIPS GDB simulator issues
@ 2026-08-16 23:43 Sebastian Huber
2026-08-16 23:43 ` [PATCH 1/4] sim: Allow an overdue event to be descheduled Sebastian Huber
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: Sebastian Huber @ 2026-08-16 23:43 UTC (permalink / raw)
To: gdb-patches
Running the RTEMS test suite using the MIPS GDB simulator surfaced some
issues in the simulator which I would like to fix.
Sebastian Huber (4):
sim: Allow an overdue event to be descheduled
sim/mips: Do not abort on a HI/LO hazard
sim/mips: Deliver the reserved instruction exception
sim/mips: Recognise a software interrupt request
sim/common/sim-events.c | 6 +++-
sim/mips/interp.c | 67 +++++++++++++++++++++++++++++++++++++++--
sim/mips/mips.igen | 17 +++++++----
sim/mips/sim-main.h | 2 ++
4 files changed, 82 insertions(+), 10 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH 1/4] sim: Allow an overdue event to be descheduled 2026-08-16 23:43 [PATCH 0/4] Fix some MIPS GDB simulator issues Sebastian Huber @ 2026-08-16 23:43 ` Sebastian Huber 2026-08-18 9:59 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard Sebastian Huber ` (2 subsequent siblings) 3 siblings, 1 reply; 13+ messages in thread From: Sebastian Huber @ 2026-08-16 23:43 UTC (permalink / raw) To: gdb-patches sim_events_deschedule() asserted that a non-empty event queue implies a non-negative time from event. That is not an invariant. sim_events_slip() decrements the time from event unconditionally, so the time can pass an event which is still queued. update_time_from_event() then computes a negative time from event for the head of the queue, which is overdue rather than pending. The next tick raises the pending work flag and sim_events_process() consumes it, so nothing is lost. An event handler which deschedules another overdue event triggers the assertion. The MIPS jmr3904 board reaches this with two timers running: they queue events for the same time, a branch delay slot slips the time past both, and the handler of the first deschedules the second. Assert only the direction which holds: an empty queue gives a negative time from event. Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> --- sim/common/sim-events.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sim/common/sim-events.c b/sim/common/sim-events.c index f87f133efc3..e908c40f764 100644 --- a/sim/common/sim-events.c +++ b/sim/common/sim-events.c @@ -852,7 +852,11 @@ sim_events_deschedule (SIM_DESC sd, (dead->trace != NULL) ? dead->trace : "")); sim_events_free (sd, dead); update_time_from_event (sd); - SIM_ASSERT ((events->time_from_event >= 0) == (events->queue != NULL)); + /* sim_events_slip() advances the time past an event + which is still queued. The head of the queue is then + overdue rather than pending. */ + SIM_ASSERT (events->queue != NULL + || events->time_from_event < 0); return; } } -- 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] sim: Allow an overdue event to be descheduled 2026-08-16 23:43 ` [PATCH 1/4] sim: Allow an overdue event to be descheduled Sebastian Huber @ 2026-08-18 9:59 ` Andrew Burgess 0 siblings, 0 replies; 13+ messages in thread From: Andrew Burgess @ 2026-08-18 9:59 UTC (permalink / raw) To: Sebastian Huber, gdb-patches Sebastian Huber <sebastian.huber@embedded-brains.de> writes: > sim_events_deschedule() asserted that a non-empty event queue implies a > non-negative time from event. That is not an invariant. > sim_events_slip() decrements the time from event unconditionally, so the > time can pass an event which is still queued. update_time_from_event() > then computes a negative time from event for the head of the queue, > which is overdue rather than pending. The next tick raises the pending > work flag and sim_events_process() consumes it, so nothing is lost. > > An event handler which deschedules another overdue event triggers the > assertion. The MIPS jmr3904 board reaches this with two timers running: > they queue events for the same time, a branch delay slot slips the time > past both, and the handler of the first deschedules the second. > > Assert only the direction which holds: an empty queue gives a negative > time from event. > > Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> > --- > sim/common/sim-events.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/sim/common/sim-events.c b/sim/common/sim-events.c > index f87f133efc3..e908c40f764 100644 > --- a/sim/common/sim-events.c > +++ b/sim/common/sim-events.c > @@ -852,7 +852,11 @@ sim_events_deschedule (SIM_DESC sd, > (dead->trace != NULL) ? dead->trace : "")); > sim_events_free (sd, dead); > update_time_from_event (sd); > - SIM_ASSERT ((events->time_from_event >= 0) == (events->queue != NULL)); > + /* sim_events_slip() advances the time past an event > + which is still queued. The head of the queue is then Two spaces after the period please. With that fixed: Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > + overdue rather than pending. */ > + SIM_ASSERT (events->queue != NULL > + || events->time_from_event < 0); > return; > } > } > -- > 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard 2026-08-16 23:43 [PATCH 0/4] Fix some MIPS GDB simulator issues Sebastian Huber 2026-08-16 23:43 ` [PATCH 1/4] sim: Allow an overdue event to be descheduled Sebastian Huber @ 2026-08-16 23:43 ` Sebastian Huber 2026-08-18 10:04 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 3/4] sim/mips: Deliver the reserved instruction exception Sebastian Huber 2026-08-16 23:43 ` [PATCH 4/4] sim/mips: Recognise a software interrupt request Sebastian Huber 3 siblings, 1 reply; 13+ messages in thread From: Sebastian Huber @ 2026-08-16 23:43 UTC (permalink / raw) To: gdb-patches check_mf_hilo() calls sim_engine_abort() when a mfhi or mflo reads a value which the ISA leaves UNPREDICTABLE. The comment above the helper states the rule correctly: the result is UNPREDICTABLE, not an error. Reading an undefined value is not a fault, and the return value of the helper is discarded at both call sites, so the abort is its only effect. The abort halts the client and the run loop resumes it on the faulting instruction, which reads the same register again and aborts again. An operating system may save HI and LO on interrupt entry and restores them unchanged, so it reads them at an arbitrary instruction boundary where the last writer is whatever the interrupted program did. Every clock tick landing in multiply or divide heavy code then deadlocks it. Warn instead. Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> --- sim/mips/mips.igen | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sim/mips/mips.igen b/sim/mips/mips.igen index 8203d19f8c4..fc2f81f85d0 100644 --- a/sim/mips/mips.igen +++ b/sim/mips/mips.igen @@ -411,12 +411,17 @@ && peer->mf.timestamp < peer->mt.timestamp)) { /* The peer has been written to since the last OP yet we have - not */ - sim_engine_abort (SD, CPU, CIA, "HILO: %s: MF at 0x%08lx following OP at 0x%08lx corrupted by MT at 0x%08lx\n", - itable[MY_INDEX].name, - (long) CIA, - (long) history->op.cia, - (long) peer->mt.cia); + not. The ISA makes the result of this MF UNPREDICTABLE, it does not + make it an error. An operating system may save HI and LO + unconditionally on interrupt entry and restores them unchanged, so it + reads whatever the interrupted program left behind. Aborting the + simulation here deadlocks such a system, since the run loop resumes at + the faulting instruction and the MF is executed again. */ + sim_io_eprintf (SD, "HILO: %s: MF at 0x%08lx following OP at 0x%08lx corrupted by MT at 0x%08lx\n", + itable[MY_INDEX].name, + (long) CIA, + (long) history->op.cia, + (long) peer->mt.cia); ok = 0; } history->mf.timestamp = time; -- 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard 2026-08-16 23:43 ` [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard Sebastian Huber @ 2026-08-18 10:04 ` Andrew Burgess 0 siblings, 0 replies; 13+ messages in thread From: Andrew Burgess @ 2026-08-18 10:04 UTC (permalink / raw) To: Sebastian Huber, gdb-patches Sebastian Huber <sebastian.huber@embedded-brains.de> writes: > check_mf_hilo() calls sim_engine_abort() when a mfhi or mflo reads a > value which the ISA leaves UNPREDICTABLE. The comment above the helper > states the rule correctly: the result is UNPREDICTABLE, not an error. > Reading an undefined value is not a fault, and the return value of the > helper is discarded at both call sites, so the abort is its only effect. > > The abort halts the client and the run loop resumes it on the faulting > instruction, which reads the same register again and aborts again. An > operating system may save HI and LO on interrupt entry and restores them 'restores' -> 'restore' please. > unchanged, so it reads them at an arbitrary instruction boundary where > the last writer is whatever the interrupted program did. Every clock > tick landing in multiply or divide heavy code then deadlocks it. > > Warn instead. > > Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> > --- > sim/mips/mips.igen | 17 +++++++++++------ > 1 file changed, 11 insertions(+), 6 deletions(-) > > diff --git a/sim/mips/mips.igen b/sim/mips/mips.igen > index 8203d19f8c4..fc2f81f85d0 100644 > --- a/sim/mips/mips.igen > +++ b/sim/mips/mips.igen > @@ -411,12 +411,17 @@ > && peer->mf.timestamp < peer->mt.timestamp)) > { > /* The peer has been written to since the last OP yet we have > - not */ > - sim_engine_abort (SD, CPU, CIA, "HILO: %s: MF at 0x%08lx following OP at 0x%08lx corrupted by MT at 0x%08lx\n", > - itable[MY_INDEX].name, > - (long) CIA, > - (long) history->op.cia, > - (long) peer->mt.cia); > + not. The ISA makes the result of this MF UNPREDICTABLE, it does not > + make it an error. An operating system may save HI and LO > + unconditionally on interrupt entry and restores them unchanged, so it Same here: 'restores' -> 'restore' please. > + reads whatever the interrupted program left behind. Aborting the > + simulation here deadlocks such a system, since the run loop resumes at > + the faulting instruction and the MF is executed again. */ > + sim_io_eprintf (SD, "HILO: %s: MF at 0x%08lx following OP at 0x%08lx corrupted by MT at 0x%08lx\n", > + itable[MY_INDEX].name, > + (long) CIA, > + (long) history->op.cia, > + (long) peer->mt.cia); This is fine, but is there not a risk that this is going to end up spamming stderr (or whatever) with these warnings? Would it not be worth adding some kind of counter: { static warning_count = 0; if (warning_count < 10) { ++warning_count; sim_io_eprintf (SD, "....."); if (warning_count == 10) sim_io_eprintf (SD, "Future warnings about .... are now silenced\n"); } } But I'll leave this up to you, I'm happy with the change as is. Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > ok = 0; > } > history->mf.timestamp = time; > -- > 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/4] sim/mips: Deliver the reserved instruction exception 2026-08-16 23:43 [PATCH 0/4] Fix some MIPS GDB simulator issues Sebastian Huber 2026-08-16 23:43 ` [PATCH 1/4] sim: Allow an overdue event to be descheduled Sebastian Huber 2026-08-16 23:43 ` [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard Sebastian Huber @ 2026-08-16 23:43 ` Sebastian Huber 2026-08-18 10:18 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 4/4] sim/mips: Recognise a software interrupt request Sebastian Huber 3 siblings, 1 reply; 13+ messages in thread From: Sebastian Huber @ 2026-08-16 23:43 UTC (permalink / raw) To: gdb-patches The ReservedInstruction and CoProcessorUnusable cases of signal_exception() compute CAUSE, SR and EPC and set PC to the exception handler address, and then assign PC = EPC, which discards all of it. The client resumes on the faulting instruction rather than in its handler, so it raises the same exception again and never advances. The address error cases immediately above deliberately do not do this and say so in a comment. The consequence is that a target which handles the exception never runs. A compiler emits rdhwr to read the thread pointer for every access to a thread-local object on every MIPS target, because the instruction exists since MIPS32r2 and the operating system is expected to emulate it where the processor lacks it. The R3900 lacks it. Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> --- sim/mips/interp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sim/mips/interp.c b/sim/mips/interp.c index a2757ec5ef7..5dbd1482b99 100644 --- a/sim/mips/interp.c +++ b/sim/mips/interp.c @@ -1992,7 +1992,8 @@ signal_exception (SIM_DESC sd, case ReservedInstruction: case CoProcessorUnusable: - PC = EPC; + /* Leave PC at the exception handler address. This allows emulating + an instruction the CPU lacks. */ sim_engine_halt (SD, CPU, NULL, PC, sim_stopped, SIM_SIGILL); -- 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] sim/mips: Deliver the reserved instruction exception 2026-08-16 23:43 ` [PATCH 3/4] sim/mips: Deliver the reserved instruction exception Sebastian Huber @ 2026-08-18 10:18 ` Andrew Burgess 0 siblings, 0 replies; 13+ messages in thread From: Andrew Burgess @ 2026-08-18 10:18 UTC (permalink / raw) To: Sebastian Huber, gdb-patches Sebastian Huber <sebastian.huber@embedded-brains.de> writes: > The ReservedInstruction and CoProcessorUnusable cases of > signal_exception() compute CAUSE, SR and EPC and set PC to the exception > handler address, and then assign PC = EPC, which discards all of it. > The client resumes on the faulting instruction rather than in its > handler, so it raises the same exception again and never advances. The > address error cases immediately above deliberately do not do this and > say so in a comment. > > The consequence is that a target which handles the exception never runs. > A compiler emits rdhwr to read the thread pointer for every access to a > thread-local object on every MIPS target, because the instruction exists > since MIPS32r2 and the operating system is expected to emulate it where > the processor lacks it. The R3900 lacks it. > > Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> Please remove the 'Signed-off-by' tag from all commits. This tag has no meaning for the GDB project, but might in the future. Sorry I missed giving this feedback on the earlier patches. Otherwise: Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > --- > sim/mips/interp.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/sim/mips/interp.c b/sim/mips/interp.c > index a2757ec5ef7..5dbd1482b99 100644 > --- a/sim/mips/interp.c > +++ b/sim/mips/interp.c > @@ -1992,7 +1992,8 @@ signal_exception (SIM_DESC sd, > > case ReservedInstruction: > case CoProcessorUnusable: > - PC = EPC; > + /* Leave PC at the exception handler address. This allows emulating > + an instruction the CPU lacks. */ > sim_engine_halt (SD, CPU, NULL, PC, > sim_stopped, SIM_SIGILL); > > -- > 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-16 23:43 [PATCH 0/4] Fix some MIPS GDB simulator issues Sebastian Huber ` (2 preceding siblings ...) 2026-08-16 23:43 ` [PATCH 3/4] sim/mips: Deliver the reserved instruction exception Sebastian Huber @ 2026-08-16 23:43 ` Sebastian Huber 2026-08-18 10:42 ` Andrew Burgess 3 siblings, 1 reply; 13+ messages in thread From: Sebastian Huber @ 2026-08-16 23:43 UTC (permalink / raw) To: gdb-patches Add support for the Cause.IP0 and Cause.IP1 software generated interrupts defined by the MIPS Architecture For Programmers Volume III: The MIPS Privileged Resource Architecture. Deliver it from the event queue rather than in place, because signal_exception() leaves the handler address in the program counter for an interrupt and the instruction being executed would overwrite it. Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> --- sim/mips/interp.c | 64 +++++++++++++++++++++++++++++++++++++++++++-- sim/mips/sim-main.h | 2 ++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/sim/mips/interp.c b/sim/mips/interp.c index 5dbd1482b99..fddb96d00ed 100644 --- a/sim/mips/interp.c +++ b/sim/mips/interp.c @@ -295,6 +295,58 @@ static const OPTION mips_options[] = int interrupt_pending; +/* An interrupt is requested while the interrupts are enabled and a pending + bit of the Cause register meets its mask bit in the Status register: + + Status.IE = 1, Status.EXL = 0, Status.ERL = 0, Cause.IP & Status.IM != 0 + + MIPS Architecture For Programmers Volume III: The MIPS Privileged Resource + Architecture, the Interrupts chapter. The R3000 generation, which the + R3900 belongs to, has no exception level and disables the interrupts by + shifting the interrupt enable stack of its Status register instead, so only + the current enable takes part; see the IDT R30xx Family Software Reference + Manual, the Status register of the CPU control chapter. */ +static int +interrupt_requested (sim_cpu *cpu) +{ + if ((SR & status_IE) == 0) + return 0; + +#ifndef SUBTARGET_R3900 + if ((SR & (status_EXL | status_ERL)) != 0) + return 0; +#endif + + /* Only the software interrupts. A hardware interrupt keeps its pending bit + set until its device is served. The device model delivers it. */ + return ((CAUSE >> cause_IPSW_shift) & (SR >> status_IM_shift) + & cause_IPSW_mask) != 0; +} + +static void +software_interrupt_event (SIM_DESC sd, void *data) +{ + sim_cpu *cpu = STATE_CPU (sd, 0); + address_word cia = CPU_PC_GET (cpu); + + /* Recheck, because the write which scheduled this may have been undone in + the meantime. */ + if (interrupt_requested (cpu)) + SignalExceptionInterrupt (0); +} + +/* Deliver a pending interrupt at the next instruction boundary. It cannot be + delivered here: signal_exception() leaves the handler address in the program + counter for an interrupt and the instruction which is being executed would + overwrite it. This is why the hardware interrupts arrive through the event + queue as well. */ +static void +check_interrupts (SIM_DESC sd, sim_cpu *cpu) +{ + if (interrupt_requested (cpu)) + sim_events_schedule (sd, 1, software_interrupt_event, NULL); +} + void interrupt_event (SIM_DESC sd, void *data) { @@ -2269,14 +2321,20 @@ decode_coproc (SIM_DESC sd, if (op == cp0_mfc0 || op == cp0_dmfc0) GPR[rt] = SR; else - SR = GPR[rt]; + { + SR = GPR[rt]; + check_interrupts (sd, cpu); + } break; /* 13 = Cause R4000 VR4100 VR4300 */ case 13: if (op == cp0_mfc0 || op == cp0_dmfc0) GPR[rt] = CAUSE; else - CAUSE = GPR[rt]; + { + CAUSE = GPR[rt]; + check_interrupts (sd, cpu); + } break; /* 14 = EPC R4000 VR4100 VR4300 */ case 14: @@ -2391,6 +2449,7 @@ decode_coproc (SIM_DESC sd, { PC = EPC; SR &= ~status_EXL; + check_interrupts (sd, cpu); } } else if (op == cp0_rfe && sel == 0x10) @@ -2401,6 +2460,7 @@ decode_coproc (SIM_DESC sd, /* shift IE/KU history bits right */ SR = LSMASKED32(SR, 31, 4) | LSINSERTED32(LSEXTRACTED32(SR, 5, 2), 3, 0); + check_interrupts (sd, cpu); /* TODO: CACHE register */ #endif /* SUBTARGET_R3900 */ diff --git a/sim/mips/sim-main.h b/sim/mips/sim-main.h index b6cb4e12258..3af02e49c0e 100644 --- a/sim/mips/sim-main.h +++ b/sim/mips/sim-main.h @@ -557,6 +557,8 @@ struct mips_sim_state { #define cause_SW1 (1 << 9) /* Software interrupt 1 */ #define cause_IP_mask (0x3f) /* Interrupt pending field */ #define cause_IP_shift (10) +#define cause_IPSW_mask (0x3) /* Software interrupt pending, IP1:IP0 */ +#define cause_IPSW_shift (8) #define cause_set_EXC(x) CAUSE = (CAUSE & ~cause_EXC_mask) | ((x << cause_EXC_shift) & cause_EXC_mask) #define cause_set_EXC2(x) CAUSE = (CAUSE & ~cause_EXC2_mask) | ((x << cause_EXC2_shift) & cause_EXC2_mask) -- 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-16 23:43 ` [PATCH 4/4] sim/mips: Recognise a software interrupt request Sebastian Huber @ 2026-08-18 10:42 ` Andrew Burgess 2026-08-18 11:45 ` Sebastian Huber 2026-08-19 0:03 ` Maciej W. Rozycki 0 siblings, 2 replies; 13+ messages in thread From: Andrew Burgess @ 2026-08-18 10:42 UTC (permalink / raw) To: Sebastian Huber, gdb-patches Sebastian Huber <sebastian.huber@embedded-brains.de> writes: > Add support for the Cause.IP0 and Cause.IP1 software generated > interrupts defined by the MIPS Architecture For Programmers Volume III: > The MIPS Privileged Resource Architecture. > > Deliver it from the event queue rather than in place, because > signal_exception() leaves the handler address in the program counter for > an interrupt and the instruction being executed would overwrite it. > > Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> > --- > sim/mips/interp.c | 64 +++++++++++++++++++++++++++++++++++++++++++-- > sim/mips/sim-main.h | 2 ++ > 2 files changed, 64 insertions(+), 2 deletions(-) > > diff --git a/sim/mips/interp.c b/sim/mips/interp.c > index 5dbd1482b99..fddb96d00ed 100644 > --- a/sim/mips/interp.c > +++ b/sim/mips/interp.c > @@ -295,6 +295,58 @@ static const OPTION mips_options[] = > > int interrupt_pending; > > +/* An interrupt is requested while the interrupts are enabled and a pending > + bit of the Cause register meets its mask bit in the Status register: > + > + Status.IE = 1, Status.EXL = 0, Status.ERL = 0, Cause.IP & Status.IM != 0 > + > + MIPS Architecture For Programmers Volume III: The MIPS Privileged Resource > + Architecture, the Interrupts chapter. The R3000 generation, which the > + R3900 belongs to, has no exception level and disables the interrupts by > + shifting the interrupt enable stack of its Status register instead, so only > + the current enable takes part; see the IDT R30xx Family Software Reference > + Manual, the Status register of the CPU control chapter. */ > +static int > +interrupt_requested (sim_cpu *cpu) > +{ > + if ((SR & status_IE) == 0) > + return 0; > + > +#ifndef SUBTARGET_R3900 > + if ((SR & (status_EXL | status_ERL)) != 0) > + return 0; > +#endif > + > + /* Only the software interrupts. A hardware interrupt keeps its pending bit > + set until its device is served. The device model delivers it. */ Two spaces after 'served.' please. > + return ((CAUSE >> cause_IPSW_shift) & (SR >> status_IM_shift) > + & cause_IPSW_mask) != 0; > +} > + > +static void > +software_interrupt_event (SIM_DESC sd, void *data) > +{ > + sim_cpu *cpu = STATE_CPU (sd, 0); > + address_word cia = CPU_PC_GET (cpu); > + > + /* Recheck, because the write which scheduled this may have been undone in > + the meantime. */ > + if (interrupt_requested (cpu)) > + SignalExceptionInterrupt (0); > +} > + > +/* Deliver a pending interrupt at the next instruction boundary. It cannot be > + delivered here: signal_exception() leaves the handler address in the program > + counter for an interrupt and the instruction which is being executed would > + overwrite it. This is why the hardware interrupts arrive through the event > + queue as well. */ > +static void > +check_interrupts (SIM_DESC sd, sim_cpu *cpu) > +{ > + if (interrupt_requested (cpu)) > + sim_events_schedule (sd, 1, software_interrupt_event, NULL); > +} > + > void > interrupt_event (SIM_DESC sd, void *data) > { > @@ -2269,14 +2321,20 @@ decode_coproc (SIM_DESC sd, > if (op == cp0_mfc0 || op == cp0_dmfc0) > GPR[rt] = SR; > else > - SR = GPR[rt]; > + { > + SR = GPR[rt]; > + check_interrupts (sd, cpu); > + } > break; > /* 13 = Cause R4000 VR4100 VR4300 */ > case 13: > if (op == cp0_mfc0 || op == cp0_dmfc0) > GPR[rt] = CAUSE; > else > - CAUSE = GPR[rt]; > + { > + CAUSE = GPR[rt]; > + check_interrupts (sd, cpu); > + } > break; > /* 14 = EPC R4000 VR4100 VR4300 */ > case 14: > @@ -2391,6 +2449,7 @@ decode_coproc (SIM_DESC sd, > { > PC = EPC; > SR &= ~status_EXL; > + check_interrupts (sd, cpu); > } This is the ERET case for handling the situation where the ERL bit is cleared. If I reproduce your patched code, but with more context, we see this: /* ERET */ if (SR & status_ERL) { /* Oops, not yet available */ sim_io_printf(sd,"Warning: ERET when SR[ERL] set not handled yet"); PC = EPC; SR &= ~status_ERL; } else { PC = EPC; SR &= ~status_EXL; check_interrupts (sd, cpu); } Now clearly the `if` block is broken, we're setting PC from the wrong place I think. But if this block _was_ ever fixed then we're going to need a check_interrupts call on that path too, right? My suggestion is that we move the check_interrupts call after the `else` block, like this: /* ERET */ if (SR & status_ERL) { /* Oops, not yet available */ sim_io_printf(sd,"Warning: ERET when SR[ERL] set not handled yet"); PC = EPC; SR &= ~status_ERL; } else { PC = EPC; SR &= ~status_EXL; } check_interrupts (sd, cpu); This doesn't fix the `if` block, but if someone ever does fix that path, then the check_interrupts call will be in place ready for them. What do you think? If you're happy to accept the two changes then: Approved-By: Andrew Burgess <aburgess@redhat.com> Thanks, Andrew > } > else if (op == cp0_rfe && sel == 0x10) > @@ -2401,6 +2460,7 @@ decode_coproc (SIM_DESC sd, > > /* shift IE/KU history bits right */ > SR = LSMASKED32(SR, 31, 4) | LSINSERTED32(LSEXTRACTED32(SR, 5, 2), 3, 0); > + check_interrupts (sd, cpu); > > /* TODO: CACHE register */ > #endif /* SUBTARGET_R3900 */ > diff --git a/sim/mips/sim-main.h b/sim/mips/sim-main.h > index b6cb4e12258..3af02e49c0e 100644 > --- a/sim/mips/sim-main.h > +++ b/sim/mips/sim-main.h > @@ -557,6 +557,8 @@ struct mips_sim_state { > #define cause_SW1 (1 << 9) /* Software interrupt 1 */ > #define cause_IP_mask (0x3f) /* Interrupt pending field */ > #define cause_IP_shift (10) > +#define cause_IPSW_mask (0x3) /* Software interrupt pending, IP1:IP0 */ > +#define cause_IPSW_shift (8) > > #define cause_set_EXC(x) CAUSE = (CAUSE & ~cause_EXC_mask) | ((x << cause_EXC_shift) & cause_EXC_mask) > #define cause_set_EXC2(x) CAUSE = (CAUSE & ~cause_EXC2_mask) | ((x << cause_EXC2_shift) & cause_EXC2_mask) > -- > 2.51.0 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-18 10:42 ` Andrew Burgess @ 2026-08-18 11:45 ` Sebastian Huber 2026-08-19 0:03 ` Maciej W. Rozycki 1 sibling, 0 replies; 13+ messages in thread From: Sebastian Huber @ 2026-08-18 11:45 UTC (permalink / raw) To: Andrew Burgess; +Cc: gdb-patches ----- Am 18. Aug 2026 um 12:42 schrieb Andrew Burgess aburgess@redhat.com: > Sebastian Huber <sebastian.huber@embedded-brains.de> writes: > >> Add support for the Cause.IP0 and Cause.IP1 software generated >> interrupts defined by the MIPS Architecture For Programmers Volume III: >> The MIPS Privileged Resource Architecture. >> >> Deliver it from the event queue rather than in place, because >> signal_exception() leaves the handler address in the program counter for >> an interrupt and the instruction being executed would overwrite it. >> >> Signed-off-by: Sebastian Huber <sebastian.huber@embedded-brains.de> >> --- >> sim/mips/interp.c | 64 +++++++++++++++++++++++++++++++++++++++++++-- >> sim/mips/sim-main.h | 2 ++ >> 2 files changed, 64 insertions(+), 2 deletions(-) >> >> diff --git a/sim/mips/interp.c b/sim/mips/interp.c >> index 5dbd1482b99..fddb96d00ed 100644 >> --- a/sim/mips/interp.c >> +++ b/sim/mips/interp.c >> @@ -295,6 +295,58 @@ static const OPTION mips_options[] = >> >> int interrupt_pending; >> >> +/* An interrupt is requested while the interrupts are enabled and a pending >> + bit of the Cause register meets its mask bit in the Status register: >> + >> + Status.IE = 1, Status.EXL = 0, Status.ERL = 0, Cause.IP & Status.IM != 0 >> + >> + MIPS Architecture For Programmers Volume III: The MIPS Privileged Resource >> + Architecture, the Interrupts chapter. The R3000 generation, which the >> + R3900 belongs to, has no exception level and disables the interrupts by >> + shifting the interrupt enable stack of its Status register instead, so only >> + the current enable takes part; see the IDT R30xx Family Software Reference >> + Manual, the Status register of the CPU control chapter. */ >> +static int >> +interrupt_requested (sim_cpu *cpu) >> +{ >> + if ((SR & status_IE) == 0) >> + return 0; >> + >> +#ifndef SUBTARGET_R3900 >> + if ((SR & (status_EXL | status_ERL)) != 0) >> + return 0; >> +#endif >> + >> + /* Only the software interrupts. A hardware interrupt keeps its pending bit >> + set until its device is served. The device model delivers it. */ > > Two spaces after 'served.' please. > >> + return ((CAUSE >> cause_IPSW_shift) & (SR >> status_IM_shift) >> + & cause_IPSW_mask) != 0; >> +} >> + >> +static void >> +software_interrupt_event (SIM_DESC sd, void *data) >> +{ >> + sim_cpu *cpu = STATE_CPU (sd, 0); >> + address_word cia = CPU_PC_GET (cpu); >> + >> + /* Recheck, because the write which scheduled this may have been undone in >> + the meantime. */ >> + if (interrupt_requested (cpu)) >> + SignalExceptionInterrupt (0); >> +} >> + >> +/* Deliver a pending interrupt at the next instruction boundary. It cannot be >> + delivered here: signal_exception() leaves the handler address in the program >> + counter for an interrupt and the instruction which is being executed would >> + overwrite it. This is why the hardware interrupts arrive through the event >> + queue as well. */ >> +static void >> +check_interrupts (SIM_DESC sd, sim_cpu *cpu) >> +{ >> + if (interrupt_requested (cpu)) >> + sim_events_schedule (sd, 1, software_interrupt_event, NULL); >> +} >> + >> void >> interrupt_event (SIM_DESC sd, void *data) >> { >> @@ -2269,14 +2321,20 @@ decode_coproc (SIM_DESC sd, >> if (op == cp0_mfc0 || op == cp0_dmfc0) >> GPR[rt] = SR; >> else >> - SR = GPR[rt]; >> + { >> + SR = GPR[rt]; >> + check_interrupts (sd, cpu); >> + } >> break; >> /* 13 = Cause R4000 VR4100 VR4300 */ >> case 13: >> if (op == cp0_mfc0 || op == cp0_dmfc0) >> GPR[rt] = CAUSE; >> else >> - CAUSE = GPR[rt]; >> + { >> + CAUSE = GPR[rt]; >> + check_interrupts (sd, cpu); >> + } >> break; >> /* 14 = EPC R4000 VR4100 VR4300 */ >> case 14: >> @@ -2391,6 +2449,7 @@ decode_coproc (SIM_DESC sd, >> { >> PC = EPC; >> SR &= ~status_EXL; >> + check_interrupts (sd, cpu); >> } > > This is the ERET case for handling the situation where the ERL bit is > cleared. If I reproduce your patched code, but with more context, we > see this: > > /* ERET */ > if (SR & status_ERL) > { > /* Oops, not yet available */ > sim_io_printf(sd,"Warning: ERET when SR[ERL] set not handled yet"); > PC = EPC; > SR &= ~status_ERL; > } > else > { > PC = EPC; > SR &= ~status_EXL; > check_interrupts (sd, cpu); > } > > Now clearly the `if` block is broken, we're setting PC from the wrong > place I think. But if this block _was_ ever fixed then we're going to > need a check_interrupts call on that path too, right? > > My suggestion is that we move the check_interrupts call after the `else` > block, like this: > > /* ERET */ > if (SR & status_ERL) > { > /* Oops, not yet available */ > sim_io_printf(sd,"Warning: ERET when SR[ERL] set not handled yet"); > PC = EPC; > SR &= ~status_ERL; > } > else > { > PC = EPC; > SR &= ~status_EXL; > } > check_interrupts (sd, cpu); > > This doesn't fix the `if` block, but if someone ever does fix that path, > then the check_interrupts call will be in place ready for them. > > What do you think? Thanks for your review. I used the RTEMS test suite as my main driver for the changes. Yes, I think that moving the check after the `if` block makes sense. > > If you're happy to accept the two changes then: > > Approved-By: Andrew Burgess <aburgess@redhat.com> I am only an occasional contributor, so I am not sure if I understood it correctly. 1. I remove the Signed-off-by from all four patches. 2. I fix all your review comments. 3. I run my tests again. 4. I add the Approved-By: Andrew Burgess <aburgess@redhat.com> to all four commits. 5. I don't send a v2 version to the patches list. 6. I check in the updated patch set directly. Kind regards, Sebastian -- embedded brains GmbH & Co. KG Herr Sebastian HUBER Dornierstr. 4 82178 Puchheim Germany email: sebastian.huber@embedded-brains.de phone: +49-89-18 94 741 - 16 fax: +49-89-18 94 741 - 08 Registergericht: Amtsgericht München Registernummer: HRB 157899 Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler Unsere Datenschutzerklärung finden Sie hier: https://embedded-brains.de/datenschutzerklaerung/ ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-18 10:42 ` Andrew Burgess 2026-08-18 11:45 ` Sebastian Huber @ 2026-08-19 0:03 ` Maciej W. Rozycki 2026-08-19 0:27 ` Sebastian Huber 1 sibling, 1 reply; 13+ messages in thread From: Maciej W. Rozycki @ 2026-08-19 0:03 UTC (permalink / raw) To: Andrew Burgess; +Cc: Sebastian Huber, gdb-patches On Tue, 18 Aug 2026, Andrew Burgess wrote: > Now clearly the `if` block is broken, we're setting PC from the wrong > place I think. Yep, the correct one would be ErrorEPC, unimplemented in sim AFAICT just as proper support for status_ERL, which has numerous implications beyond just ERET. Maciej ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-19 0:03 ` Maciej W. Rozycki @ 2026-08-19 0:27 ` Sebastian Huber 2026-08-19 13:17 ` Maciej W. Rozycki 0 siblings, 1 reply; 13+ messages in thread From: Sebastian Huber @ 2026-08-19 0:27 UTC (permalink / raw) To: Maciej W. Rozycki; +Cc: Andrew Burgess, gdb-patches ----- Am 19. Aug 2026 um 2:03 schrieb Maciej W. Rozycki macro@orcam.me.uk: > On Tue, 18 Aug 2026, Andrew Burgess wrote: > >> Now clearly the `if` block is broken, we're setting PC from the wrong >> place I think. > > Yep, the correct one would be ErrorEPC, unimplemented in sim AFAICT just > as proper support for status_ERL, which has numerous implications beyond > just ERET. I this something I should address within my patch set or is moving the check_interrupts() after the if/else block enough? -- embedded brains GmbH & Co. KG Herr Sebastian HUBER Dornierstr. 4 82178 Puchheim Germany email: sebastian.huber@embedded-brains.de phone: +49-89-18 94 741 - 16 fax: +49-89-18 94 741 - 08 Registergericht: Amtsgericht München Registernummer: HRB 157899 Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler Unsere Datenschutzerklärung finden Sie hier: https://embedded-brains.de/datenschutzerklaerung/ ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] sim/mips: Recognise a software interrupt request 2026-08-19 0:27 ` Sebastian Huber @ 2026-08-19 13:17 ` Maciej W. Rozycki 0 siblings, 0 replies; 13+ messages in thread From: Maciej W. Rozycki @ 2026-08-19 13:17 UTC (permalink / raw) To: Sebastian Huber; +Cc: Andrew Burgess, gdb-patches On Wed, 19 Aug 2026, Sebastian Huber wrote: > >> Now clearly the `if` block is broken, we're setting PC from the wrong > >> place I think. > > > > Yep, the correct one would be ErrorEPC, unimplemented in sim AFAICT just > > as proper support for status_ERL, which has numerous implications beyond > > just ERET. > > I this something I should address within my patch set or is moving the > check_interrupts() after the if/else block enough? I have glanced over your change and I conclude that the idea here is to call check_interrupts() whenever CP0 Status or Config registers have been changed such as to possibly raise a software interrupt. Given how sim has been structured this seems a reasonable approach to me. For this I think just moving the call past the block is exactly what is needed. Also check_interrupts() needs to be called from DERET emulation, since clearing CP0 Debug.DM (Debug_DM) also re-enables interrupts (subject to CP0 Status conditions), and Debug_DM needs to be checked against in `interrupt_requested'. FWIW I think this would best be called `software_interrupt_requested' or suchlike to avoid the ambiguity. While one can read the comment, there's no need to force them doing so to figure out the intent. Then there's the preexisting issue with `interrupt_event' that does not check any of this beyond just CP0 Status.IE (status_IE). While it seems broken to me anyway, as I fail to see a place where `interrupt_pending' would be set, let's not let breakage accumulate. So I think bits from your `interrupt_requested' need to be factored out and used at both places, possibly as an introductory change. It's not clear to me how the argument to SignalExceptionInterrupt() is supposed to be used, but it's not interpreted anyway AFAICT, so let's just leave it as it is. As to adding proper support for ErrorEPC/status_ERL, you are obviously welcome to if you feel so inclined and in particular have a use for, but I wouldn't consider it a prerequisite for your patchset. In real hardware it's a feature used primarily in the context of handling CPU soft reset, NMI and cache error exceptions, which I think are secondary ISA features when it comes to sim. Maciej ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-08-19 13:18 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-08-16 23:43 [PATCH 0/4] Fix some MIPS GDB simulator issues Sebastian Huber 2026-08-16 23:43 ` [PATCH 1/4] sim: Allow an overdue event to be descheduled Sebastian Huber 2026-08-18 9:59 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 2/4] sim/mips: Do not abort on a HI/LO hazard Sebastian Huber 2026-08-18 10:04 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 3/4] sim/mips: Deliver the reserved instruction exception Sebastian Huber 2026-08-18 10:18 ` Andrew Burgess 2026-08-16 23:43 ` [PATCH 4/4] sim/mips: Recognise a software interrupt request Sebastian Huber 2026-08-18 10:42 ` Andrew Burgess 2026-08-18 11:45 ` Sebastian Huber 2026-08-19 0:03 ` Maciej W. Rozycki 2026-08-19 0:27 ` Sebastian Huber 2026-08-19 13:17 ` Maciej W. Rozycki
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox