diff mbox series

[8/8] q800: add NMI handler

Message ID 20211013212132.31519-9-mark.cave-ayland@ilande.co.uk
State New
Headers show
Series q800: GLUE updates for A/UX mode | expand

Commit Message

Mark Cave-Ayland Oct. 13, 2021, 9:21 p.m. UTC
This allows the programmer's switch to be triggered via the monitor for debugging
purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold
the NMI active for 100ms before releasing it again.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
 hw/m68k/q800.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

Comments

Laurent Vivier Oct. 15, 2021, 8:40 a.m. UTC | #1
Le 13/10/2021 à 23:21, Mark Cave-Ayland a écrit :
> This allows the programmer's switch to be triggered via the monitor for debugging
> purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold
> the NMI active for 100ms before releasing it again.
> 

I'm wondering if Qemu provides another way to have a level-triggered interrupt in this case.

I' tried to see if keeping the button pressed on a mac kept the IRQ up (as QMP NMI does), but a real
mac is too slow and has to many things to display it was not really conclusive...

Thanks,
Laurent
Mark Cave-Ayland Oct. 15, 2021, 8:12 p.m. UTC | #2
On 15/10/2021 09:40, Laurent Vivier wrote:

> Le 13/10/2021 à 23:21, Mark Cave-Ayland a écrit :
>> This allows the programmer's switch to be triggered via the monitor for debugging
>> purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold
>> the NMI active for 100ms before releasing it again.
> 
> I'm wondering if Qemu provides another way to have a level-triggered interrupt in this case.
> 
> I' tried to see if keeping the button pressed on a mac kept the IRQ up (as QMP NMI does), but a real
> mac is too slow and has to many things to display it was not really conclusive...

When writing the patch I rebased the outstanding MacOS patches onto the branch, 
installed Macsbug into MacOS and used "info nmi" to break into it.

Testing glue_nmi() with:

   GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
   GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);

i.e. a simple pulse didn't launch MacsBug at all. Keeping the NMI high launches 
MacsBug (which is usable) but then as soon as you exit MacsBug with ES, MacsBug 
breaks immediately again making it impossible to return to the Finder. Adding the 
timer allows launching MacsBug and then exiting/re-entering MacsBug again on demand 
as expected.


ATB,

Mark.
Laurent Vivier Oct. 16, 2021, 5:09 p.m. UTC | #3
Le 15/10/2021 à 22:12, Mark Cave-Ayland a écrit :
> On 15/10/2021 09:40, Laurent Vivier wrote:
> 
>> Le 13/10/2021 à 23:21, Mark Cave-Ayland a écrit :
>>> This allows the programmer's switch to be triggered via the monitor for debugging
>>> purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold
>>> the NMI active for 100ms before releasing it again.
>>
>> I'm wondering if Qemu provides another way to have a level-triggered interrupt in this case.
>>
>> I' tried to see if keeping the button pressed on a mac kept the IRQ up (as QMP NMI does), but a real
>> mac is too slow and has to many things to display it was not really conclusive...
> 
> When writing the patch I rebased the outstanding MacOS patches onto the branch, installed Macsbug
> into MacOS and used "info nmi" to break into it.
> 
> Testing glue_nmi() with:
> 
>   GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
>   GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);
> 
> i.e. a simple pulse didn't launch MacsBug at all. Keeping the NMI high launches MacsBug (which is
> usable) but then as soon as you exit MacsBug with ES, MacsBug breaks immediately again making it
> impossible to return to the Finder. Adding the timer allows launching MacsBug and then
> exiting/re-entering MacsBug again on demand as expected.
> 

I think we have to mimic the finger pressing the button..

By the way, NMI should also dumps the CPU registers under linux.

Reviewied-by: Laurent Vivier <laurent@vivier.eu>
Mark Cave-Ayland Oct. 17, 2021, 10 a.m. UTC | #4
On 16/10/2021 18:09, Laurent Vivier wrote:

> Le 15/10/2021 à 22:12, Mark Cave-Ayland a écrit :
>> On 15/10/2021 09:40, Laurent Vivier wrote:
>>
>>> Le 13/10/2021 à 23:21, Mark Cave-Ayland a écrit :
>>>> This allows the programmer's switch to be triggered via the monitor for debugging
>>>> purposes. Since the CPU level 7 interrupt is level-triggered, use a timer to hold
>>>> the NMI active for 100ms before releasing it again.
>>>
>>> I'm wondering if Qemu provides another way to have a level-triggered interrupt in this case.
>>>
>>> I' tried to see if keeping the button pressed on a mac kept the IRQ up (as QMP NMI does), but a real
>>> mac is too slow and has to many things to display it was not really conclusive...
>>
>> When writing the patch I rebased the outstanding MacOS patches onto the branch, installed Macsbug
>> into MacOS and used "info nmi" to break into it.
>>
>> Testing glue_nmi() with:
>>
>>    GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
>>    GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);
>>
>> i.e. a simple pulse didn't launch MacsBug at all. Keeping the NMI high launches MacsBug (which is
>> usable) but then as soon as you exit MacsBug with ES, MacsBug breaks immediately again making it
>> impossible to return to the Finder. Adding the timer allows launching MacsBug and then
>> exiting/re-entering MacsBug again on demand as expected.
>>
> 
> I think we have to mimic the finger pressing the button..
> 
> By the way, NMI should also dumps the CPU registers under linux.
> 
> Reviewied-by: Laurent Vivier <laurent@vivier.eu>

I've just tried this on Linux, and it seems to work okay although I get several 
copies of the register dump on the console for a single invocation of "info nmi" e.g.

[    4.610000] Non-Maskable Interrupt
[    4.610000] Modules linked in: mac_esp(+) esp_scsi macsonic
[    4.610000] PC: [<00002dc0>] arch_cpu_idle+0x4/0x6
[    4.610000] SR: 2000  SP: (ptrval)  a2: 00395314
[    4.610000] d0: 00000000    d1: 00000002    d2: 0004b492    d3: 00392000
[    4.610000] d4: 00000000    d5: 00000000    a0: 00392000    a1: 00395314
[    4.610000] Process swapper (pid: 0, task=(ptrval))
[    4.610000] Frame format=0
[    4.610000] Stack from 00393f9c:
[    4.610000]         0004b540 00000431 0040ee22 0004b4c4 0002bf6a 004005f0 00393ff8 
0004b714
[    4.610000]         0003e23a 0029c518 000000c2 0040eb54 3dc1e5d0 00000000 0004ff74 
0003efe2
[    4.610000]         003f4e92 00000000 00000040 00000000 00000000 00000000 0040ee22 
00000000
[    4.610000]         003f3872
[    4.610000] Call Trace: [<0004b540>] do_idle+0x7c/0xd2
[    4.610000]  [<0004b4c4>] do_idle+0x0/0xd2
[    4.610000]  [<0002bf6a>] kernel_thread+0x0/0x26
[    4.610000]  [<004005f0>] __alloc_bootmem+0x0/0x38
[    4.610000]  [<0004b714>] cpu_startup_entry+0xe/0x12
[    4.610000]  [<0003e23a>] find_task_by_pid_ns+0x0/0x22
[    4.610000]  [<0029c518>] rest_init+0x78/0x82
[    4.610000]  [<0004ff74>] printk+0x0/0x18
[    4.610000]  [<0003efe2>] parse_args+0x0/0x2c2
[    4.610000]  [<003f4e92>] start_kernel+0x43e/0x448
[    4.610000]  [<003f3872>] _sinittext+0x872/0x11f8
[    4.610000] Code: bc00 0060 f210 f0ff 7001 60cc 4e72 2000 <4e75> 2079 003b fc6c 
4a88 6702 4e90 60fe 2079 003b fc68 4a88 6702 4e90 60fe 2f0c

(repeated 7 or 8 times)

MacsBug seems to handle this better: as soon as the NMI is triggered, MacsBug opens 
and displays "NMI" both if the programmer switch GPIO is held active, or pulsed as 
implemented in the patch. When using the above patch, subsequent NMI triggers do not 
display "NMI" or change MacsBug in any visible way which suggests that once the NMI 
is triggered, the NMI is ignored until the programmers switch GPIO is inactive once 
again.

Do you see the repeated register output in Linux for a single press of the 
programmers switch on a real Quadra 800? It may be that Linux could be improved by 
having similar logic.


ATB,

Mark.
Laurent Vivier Oct. 17, 2021, 4:56 p.m. UTC | #5
Le 17/10/2021 à 12:00, Mark Cave-Ayland a écrit :
...
> I've just tried this on Linux, and it seems to work okay although I get several copies of the
> register dump on the console for a single invocation of "info nmi" e.g.
> 
> [    4.610000] Non-Maskable Interrupt
> [    4.610000] Modules linked in: mac_esp(+) esp_scsi macsonic
> [    4.610000] PC: [<00002dc0>] arch_cpu_idle+0x4/0x6
> [    4.610000] SR: 2000  SP: (ptrval)  a2: 00395314
> [    4.610000] d0: 00000000    d1: 00000002    d2: 0004b492    d3: 00392000
> [    4.610000] d4: 00000000    d5: 00000000    a0: 00392000    a1: 00395314
> [    4.610000] Process swapper (pid: 0, task=(ptrval))
> [    4.610000] Frame format=0
> [    4.610000] Stack from 00393f9c:
> [    4.610000]         0004b540 00000431 0040ee22 0004b4c4 0002bf6a 004005f0 00393ff8 0004b714
> [    4.610000]         0003e23a 0029c518 000000c2 0040eb54 3dc1e5d0 00000000 0004ff74 0003efe2
> [    4.610000]         003f4e92 00000000 00000040 00000000 00000000 00000000 0040ee22 00000000
> [    4.610000]         003f3872
> [    4.610000] Call Trace: [<0004b540>] do_idle+0x7c/0xd2
> [    4.610000]  [<0004b4c4>] do_idle+0x0/0xd2
> [    4.610000]  [<0002bf6a>] kernel_thread+0x0/0x26
> [    4.610000]  [<004005f0>] __alloc_bootmem+0x0/0x38
> [    4.610000]  [<0004b714>] cpu_startup_entry+0xe/0x12
> [    4.610000]  [<0003e23a>] find_task_by_pid_ns+0x0/0x22
> [    4.610000]  [<0029c518>] rest_init+0x78/0x82
> [    4.610000]  [<0004ff74>] printk+0x0/0x18
> [    4.610000]  [<0003efe2>] parse_args+0x0/0x2c2
> [    4.610000]  [<003f4e92>] start_kernel+0x43e/0x448
> [    4.610000]  [<003f3872>] _sinittext+0x872/0x11f8
> [    4.610000] Code: bc00 0060 f210 f0ff 7001 60cc 4e72 2000 <4e75> 2079 003b fc6c 4a88 6702 4e90
> 60fe 2079 003b fc68 4a88 6702 4e90 60fe 2f0c
> 
> (repeated 7 or 8 times)
> 
> MacsBug seems to handle this better: as soon as the NMI is triggered, MacsBug opens and displays
> "NMI" both if the programmer switch GPIO is held active, or pulsed as implemented in the patch. When
> using the above patch, subsequent NMI triggers do not display "NMI" or change MacsBug in any visible
> way which suggests that once the NMI is triggered, the NMI is ignored until the programmers switch
> GPIO is inactive once again.
> 
> Do you see the repeated register output in Linux for a single press of the programmers switch on a
> real Quadra 800? It may be that Linux could be improved by having similar logic.

In fact, register output is repeated while I keep the button pressed (5.14 kernel provided by Finn)

If I press and release one (or several) time(s) before the end of the first output I have only one
output.

Thanks,
Laurent
Mark Cave-Ayland Oct. 20, 2021, 1:32 p.m. UTC | #6
On 17/10/2021 17:56, Laurent Vivier wrote:

> Le 17/10/2021 à 12:00, Mark Cave-Ayland a écrit :
> ...
>> I've just tried this on Linux, and it seems to work okay although I get several copies of the
>> register dump on the console for a single invocation of "info nmi" e.g.
>>
>> [    4.610000] Non-Maskable Interrupt
>> [    4.610000] Modules linked in: mac_esp(+) esp_scsi macsonic
>> [    4.610000] PC: [<00002dc0>] arch_cpu_idle+0x4/0x6
>> [    4.610000] SR: 2000  SP: (ptrval)  a2: 00395314
>> [    4.610000] d0: 00000000    d1: 00000002    d2: 0004b492    d3: 00392000
>> [    4.610000] d4: 00000000    d5: 00000000    a0: 00392000    a1: 00395314
>> [    4.610000] Process swapper (pid: 0, task=(ptrval))
>> [    4.610000] Frame format=0
>> [    4.610000] Stack from 00393f9c:
>> [    4.610000]         0004b540 00000431 0040ee22 0004b4c4 0002bf6a 004005f0 00393ff8 0004b714
>> [    4.610000]         0003e23a 0029c518 000000c2 0040eb54 3dc1e5d0 00000000 0004ff74 0003efe2
>> [    4.610000]         003f4e92 00000000 00000040 00000000 00000000 00000000 0040ee22 00000000
>> [    4.610000]         003f3872
>> [    4.610000] Call Trace: [<0004b540>] do_idle+0x7c/0xd2
>> [    4.610000]  [<0004b4c4>] do_idle+0x0/0xd2
>> [    4.610000]  [<0002bf6a>] kernel_thread+0x0/0x26
>> [    4.610000]  [<004005f0>] __alloc_bootmem+0x0/0x38
>> [    4.610000]  [<0004b714>] cpu_startup_entry+0xe/0x12
>> [    4.610000]  [<0003e23a>] find_task_by_pid_ns+0x0/0x22
>> [    4.610000]  [<0029c518>] rest_init+0x78/0x82
>> [    4.610000]  [<0004ff74>] printk+0x0/0x18
>> [    4.610000]  [<0003efe2>] parse_args+0x0/0x2c2
>> [    4.610000]  [<003f4e92>] start_kernel+0x43e/0x448
>> [    4.610000]  [<003f3872>] _sinittext+0x872/0x11f8
>> [    4.610000] Code: bc00 0060 f210 f0ff 7001 60cc 4e72 2000 <4e75> 2079 003b fc6c 4a88 6702 4e90
>> 60fe 2079 003b fc68 4a88 6702 4e90 60fe 2f0c
>>
>> (repeated 7 or 8 times)
>>
>> MacsBug seems to handle this better: as soon as the NMI is triggered, MacsBug opens and displays
>> "NMI" both if the programmer switch GPIO is held active, or pulsed as implemented in the patch. When
>> using the above patch, subsequent NMI triggers do not display "NMI" or change MacsBug in any visible
>> way which suggests that once the NMI is triggered, the NMI is ignored until the programmers switch
>> GPIO is inactive once again.
>>
>> Do you see the repeated register output in Linux for a single press of the programmers switch on a
>> real Quadra 800? It may be that Linux could be improved by having similar logic.
> 
> In fact, register output is repeated while I keep the button pressed (5.14 kernel provided by Finn)
> 
> If I press and release one (or several) time(s) before the end of the first output I have only one
> output.

Thanks, that makes sense - my guess is that the repeated output in QEMU is just a 
symptom of the QEMU machine being much faster than the real one.


ATB,

Mark.
diff mbox series

Patch

diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
index fa851e2ec9..fca2127ddd 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -28,6 +28,7 @@ 
 #include "cpu.h"
 #include "hw/boards.h"
 #include "hw/or-irq.h"
+#include "hw/nmi.h"
 #include "elf.h"
 #include "hw/loader.h"
 #include "ui/console.h"
@@ -102,12 +103,14 @@  struct GLUEState {
     uint8_t ipr;
     uint8_t auxmode;
     qemu_irq irqs[1];
+    QEMUTimer *nmi_release;
 };
 
 #define GLUE_IRQ_IN_VIA1       0
 #define GLUE_IRQ_IN_VIA2       1
 #define GLUE_IRQ_IN_SONIC      2
 #define GLUE_IRQ_IN_ESCC       3
+#define GLUE_IRQ_IN_NMI        4
 
 #define GLUE_IRQ_NUBUS_9       0
 
@@ -167,6 +170,10 @@  static void GLUE_set_irq(void *opaque, int irq, int level)
             irq = 3;
             break;
 
+        case GLUE_IRQ_IN_NMI:
+            irq = 6;
+            break;
+
         default:
             g_assert_not_reached();
         }
@@ -192,6 +199,10 @@  static void GLUE_set_irq(void *opaque, int irq, int level)
             irq = 3;
             break;
 
+        case GLUE_IRQ_IN_NMI:
+            irq = 6;
+            break;
+
         default:
             g_assert_not_reached();
         }
@@ -223,12 +234,30 @@  static void glue_auxmode_set_irq(void *opaque, int irq, int level)
     s->auxmode = level;
 }
 
+static void glue_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+    GLUEState *s = GLUE(n);
+
+    /* Hold NMI active for 100ms */
+    GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
+    timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100);
+}
+
+static void glue_nmi_release(void *opaque)
+{
+    GLUEState *s = GLUE(opaque);
+
+    GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);
+}
+
 static void glue_reset(DeviceState *dev)
 {
     GLUEState *s = GLUE(dev);
 
     s->ipr = 0;
     s->auxmode = 0;
+
+    timer_del(s->nmi_release);
 }
 
 static const VMStateDescription vmstate_glue = {
@@ -238,6 +267,7 @@  static const VMStateDescription vmstate_glue = {
     .fields = (VMStateField[]) {
         VMSTATE_UINT8(ipr, GLUEState),
         VMSTATE_UINT8(auxmode, GLUEState),
+        VMSTATE_TIMER_PTR(nmi_release, GLUEState),
         VMSTATE_END_OF_LIST(),
     },
 };
@@ -253,6 +283,13 @@  static Property glue_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };
 
+static void glue_finalize(Object *obj)
+{
+    GLUEState *s = GLUE(obj);
+
+    timer_free(s->nmi_release);
+}
+
 static void glue_init(Object *obj)
 {
     DeviceState *dev = DEVICE(obj);
@@ -262,15 +299,20 @@  static void glue_init(Object *obj)
     qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1);
 
     qdev_init_gpio_out(dev, s->irqs, 1);
+
+    /* NMI release timer */
+    s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s);
 }
 
 static void glue_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
+    NMIClass *nc = NMI_CLASS(klass);
 
     dc->vmsd = &vmstate_glue;
     dc->reset = glue_reset;
     device_class_set_props(dc, glue_properties);
+    nc->nmi_monitor_handler = glue_nmi;
 }
 
 static const TypeInfo glue_info = {
@@ -278,7 +320,12 @@  static const TypeInfo glue_info = {
     .parent = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(GLUEState),
     .instance_init = glue_init,
+    .instance_finalize = glue_finalize,
     .class_init = glue_class_init,
+    .interfaces = (InterfaceInfo[]) {
+         { TYPE_NMI },
+         { }
+    },
 };
 
 static void main_cpu_reset(void *opaque)