Skip to content

Commit 19317d8

Browse files
authored
Merge pull request #1498 from nicolasnoble/irq-auto-black-hole
Fixing lost IRQ problem.
2 parents 4b493ab + 33232ca commit 19317d8

File tree

4 files changed

+83
-21
lines changed

4 files changed

+83
-21
lines changed

src/mips/common/syscalls/syscalls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ static __attribute__((always_inline)) int syscall_enqueueIrqHandler(int priority
531531
return ((int (*)(int))0xc0)(priority);
532532
}
533533

534+
// This syscall is broken beyond repair, as the kernel code contains a race
535+
// condition that can cause the kernel to lose IRQs. Please don't use it.
534536
static __attribute__((always_inline)) void syscall_setIrqAutoAck(uint32_t irq, int value) {
535537
register int n asm("t1") = 0x0d;
536538
__asm__ volatile("" : "=r"(n) : "r"(n));

src/mips/openbios/handlers/irq.c

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,31 @@ SOFTWARE.
3838

3939
static int s_IRQsAutoAck[11];
4040

41+
#if 0
4142
static __attribute__((section(".ramtext"))) int IRQVerifier(void) {
42-
// The original code does read IMASK and IREG for every if,
43-
// and recompute that same mask for every of them, which is
44-
// a big waste of cycles. Since IMASK and IREG are volatiles,
45-
// they can't be cached by the compiler, if this is what the
46-
// original author was thinking.
47-
uint32_t mask = IMASK & IREG;
48-
if ((mask & IRQ_CDROM) != 0) deliverEvent(EVENT_CDROM, 0x1000);
49-
if ((mask & IRQ_SPU) != 0) deliverEvent(EVENT_SPU, 0x1000);
50-
if ((mask & IRQ_GPU) != 0) deliverEvent(EVENT_GPU, 0x1000);
51-
if ((mask & IRQ_PIO) != 0) deliverEvent(EVENT_PIO, 0x1000);
52-
if ((mask & IRQ_SIO) != 0) deliverEvent(EVENT_SIO, 0x1000);
53-
if ((mask & IRQ_VBLANK) != 0) deliverEvent(EVENT_VBLANK, 0x1000);
54-
if ((mask & IRQ_TIMER0) != 0) deliverEvent(EVENT_RTC0, 0x1000);
55-
if ((mask & IRQ_TIMER1) != 0)
56-
deliverEvent(EVENT_RTC1, 0x1000); // Yes that's a copy-paste mistake from the BIOS code directly.
57-
if ((mask & IRQ_TIMER2) != 0) deliverEvent(EVENT_RTC1, 0x1000); // Keeping it this way to avoid breaking stuff.
58-
if ((mask & IRQ_CONTROLLER) != 0) deliverEvent(EVENT_CONTROLLER, 0x1000);
59-
if ((mask & IRQ_DMA) != 0) deliverEvent(EVENT_DMA, 0x1000);
43+
// This is the original code from the retail BIOS, which is broken
44+
// beyond repair. There is a race condition between the IRQs being
45+
// checked and the IRQs being acknowledged. If an IRQ is issued
46+
// after the IRQ is checked to call the event delivery, but before
47+
// the IRQ is acknowledged, the IRQ will be lost. Disabling IRQs
48+
// with cop0 will not help, as it merely prevents the CPU from
49+
// jumping to the handler at 0x80, but the IREG register will still
50+
// be mutated by the hardware. The only way to fix this is to
51+
// acknowledge the IRQs during the same if statement that checks
52+
// for them, which is what the code after the #else does.
53+
// This defunct code is kept here for reference.
54+
if ((IMASK & IREG & IRQ_CDROM) != 0) deliverEvent(EVENT_CDROM, 0x1000);
55+
if ((IMASK & IREG & IRQ_SPU) != 0) deliverEvent(EVENT_SPU, 0x1000);
56+
if ((IMASK & IREG & IRQ_GPU) != 0) deliverEvent(EVENT_GPU, 0x1000);
57+
if ((IMASK & IREG & IRQ_PIO) != 0) deliverEvent(EVENT_PIO, 0x1000);
58+
if ((IMASK & IREG & IRQ_SIO) != 0) deliverEvent(EVENT_SIO, 0x1000);
59+
if ((IMASK & IREG & IRQ_VBLANK) != 0) deliverEvent(EVENT_VBLANK, 0x1000);
60+
if ((IMASK & IREG & IRQ_TIMER0) != 0) deliverEvent(EVENT_RTC0, 0x1000);
61+
if ((IMASK & IREG & IRQ_TIMER1) != 0) deliverEvent(EVENT_RTC1, 0x1000);
62+
// Yes that's a copy-paste mistake from the BIOS code directly.
63+
if ((IMASK & IREG & IRQ_TIMER2) != 0) deliverEvent(EVENT_RTC1, 0x1000);
64+
if ((IMASK & IREG & IRQ_CONTROLLER) != 0) deliverEvent(EVENT_CONTROLLER, 0x1000);
65+
if ((IMASK & IREG & IRQ_DMA) != 0) deliverEvent(EVENT_DMA, 0x1000);
6066
uint32_t ackMask = 0;
6167
int* ptr = s_IRQsAutoAck;
6268
for (int IRQ = 0; IRQ < 11; IRQ++, ptr++) {
@@ -65,6 +71,58 @@ static __attribute__((section(".ramtext"))) int IRQVerifier(void) {
6571
IREG = ~ackMask;
6672
return 0;
6773
}
74+
#else
75+
static __attribute__((section(".ramtext"))) int IRQVerifier(void) {
76+
// This version of the IRQ verifier is a bit bigger, but it's
77+
// guaranteed to not lose any IRQs.
78+
if ((IMASK & IREG & IRQ_CDROM) != 0) {
79+
deliverEvent(EVENT_CDROM, 0x1000);
80+
if (s_IRQsAutoAck[IRQ_CDROM]) IREG &= ~IRQ_CDROM;
81+
}
82+
if ((IMASK & IREG & IRQ_SPU) != 0) {
83+
deliverEvent(EVENT_SPU, 0x1000);
84+
if (s_IRQsAutoAck[IRQ_SPU]) IREG &= ~IRQ_SPU;
85+
}
86+
if ((IMASK & IREG & IRQ_GPU) != 0) {
87+
deliverEvent(EVENT_GPU, 0x1000);
88+
if (s_IRQsAutoAck[IRQ_GPU]) IREG &= ~IRQ_GPU;
89+
}
90+
if ((IMASK & IREG & IRQ_PIO) != 0) {
91+
deliverEvent(EVENT_PIO, 0x1000);
92+
if (s_IRQsAutoAck[IRQ_PIO]) IREG &= ~IRQ_PIO;
93+
}
94+
if ((IMASK & IREG & IRQ_SIO) != 0) {
95+
deliverEvent(EVENT_SIO, 0x1000);
96+
if (s_IRQsAutoAck[IRQ_SIO]) IREG &= ~IRQ_SIO;
97+
}
98+
if ((IMASK & IREG & IRQ_VBLANK) != 0) {
99+
deliverEvent(EVENT_VBLANK, 0x1000);
100+
if (s_IRQsAutoAck[IRQ_VBLANK]) IREG &= ~IRQ_VBLANK;
101+
}
102+
if ((IMASK & IREG & IRQ_TIMER0) != 0) {
103+
deliverEvent(EVENT_RTC0, 0x1000);
104+
if (s_IRQsAutoAck[IRQ_TIMER0]) IREG &= ~IRQ_TIMER0;
105+
}
106+
if ((IMASK & IREG & IRQ_TIMER1) != 0) {
107+
deliverEvent(EVENT_RTC1, 0x1000);
108+
if (s_IRQsAutoAck[IRQ_TIMER1]) IREG &= ~IRQ_TIMER1;
109+
}
110+
if ((IMASK & IREG & IRQ_TIMER2) != 0) {
111+
// Keeping this copy/paste mistake this way to avoid breaking stuff.
112+
deliverEvent(EVENT_RTC1, 0x1000);
113+
if (s_IRQsAutoAck[IRQ_TIMER2]) IREG &= ~IRQ_TIMER2;
114+
}
115+
if ((IMASK & IREG & IRQ_CONTROLLER) != 0) {
116+
deliverEvent(EVENT_CONTROLLER, 0x1000);
117+
if (s_IRQsAutoAck[IRQ_CONTROLLER]) IREG &= ~IRQ_CONTROLLER;
118+
}
119+
if ((IMASK & IREG & IRQ_DMA) != 0) {
120+
deliverEvent(EVENT_DMA, 0x1000);
121+
if (s_IRQsAutoAck[IRQ_DMA]) IREG &= ~IRQ_DMA;
122+
}
123+
return 0;
124+
}
125+
#endif
68126

69127
static struct HandlerInfo s_IRQHandlerInfo = {
70128
.next = NULL,

src/mips/psyqo/src/cdrom-device.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ SOFTWARE.
4040
void psyqo::CDRomDevice::prepare() {
4141
Hardware::CPU::IMask.set(Hardware::CPU::IRQ::CDRom);
4242
Kernel::enableDma(Kernel::DMA::CDRom);
43-
m_event = Kernel::openEvent(EVENT_CDROM, 0x1000, EVENT_MODE_CALLBACK, [this]() { irq(); });
44-
syscall_setIrqAutoAck(2, 1);
43+
m_event = Kernel::openEvent(EVENT_CDROM, 0x1000, EVENT_MODE_CALLBACK, [this]() {
44+
Hardware::CPU::IReg.clear(Hardware::CPU::IRQ::CDRom);
45+
irq();
46+
});
4547
syscall_enableEvent(m_event);
4648
}
4749

src/mips/psyqo/src/kernel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ void psyqo::Kernel::Internal::prepare() {
161161
syscall_dequeueCDRomHandlers();
162162
syscall_setDefaultExceptionJmpBuf();
163163
uint32_t event = syscall_openEvent(EVENT_DMA, 0x1000, EVENT_MODE_CALLBACK, []() {
164+
Hardware::CPU::IReg.clear(Hardware::CPU::IRQ::DMA);
164165
uint32_t dicr = Hardware::CPU::DICR;
165166
uint32_t dirqs = dicr >> 24;
166167
dicr &= 0xff7fff;
@@ -192,7 +193,6 @@ void psyqo::Kernel::Internal::prepare() {
192193
dicr &= 0xffffff;
193194
dicr |= 0x800000;
194195
Hardware::CPU::DICR = dicr;
195-
syscall_setIrqAutoAck(3, 1);
196196

197197
for (auto& i : getInitializers()) i();
198198
}

0 commit comments

Comments
 (0)