Subject: null dereference in interrupt handler while attaching uhci
To: None <tech-kern@netbsd.org>
From: Gavan Fantom <gavan@coolfactor.org>
List: tech-kern
Date: 11/23/2007 02:11:41
I'm seeing uhci crashing during attach on my laptop with current since 
October 7th. This seems to have been exposed by the merge of intr.c and 
ioapic.c from the jmcneill-pm branch.

uhci0 at pci0 dev 29 function 0: Intel 82801GB/GR USB UHCI Controller 
(rev. 0x02)
uhci: interrupting at ioapic0 pin 23 (irq 5)
uvm_fault(0xc0a5ce80, 0, 1) -> 0xe
kernel: supervisor trap page fault, code=0
Stopped in pid 0.1 (system) at  netbsd:softintr_schedule+0x12:  movl 
0x8(%ebx),%esi
db> tr
softintr_schedule(0,d8800,2,3,3) at netbsd:softintr_schedule+0x12
uhci_intr1(c20f700,c0b587a8,0,1010e,0) at netbsd:uhci_intr1+0x123
DDB lost frame for netbsd:Xintr_ioapic_level5+0xac, trying 0xcba29ff4
Xintr_ioapic_level5() at netbsd:Xintr_ioapic_level5+0xac
--- interrupt ---
--- switch to interrupt stack ---
Xspllower(0,0,0,2000,c20f7000) at netbsd:Xspllower+0xf
pci_conf_read(0,8000e800,60,2000,c20f7000) at netbsd:pci_conf_read+0xa6
uhci_pci_attach(c1dc4800,c20f7000,c0b589cc,c0b58a08,c0b589cc) at 
netbsd:uhci_pci_attach+0x21b
...


The problematic fragment of code comes here (in uhci_pci_attach):

        sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, uhci_intr, sc);
        if (sc->sc_ih == NULL) {
             ...
        }
        aprint_normal("%s: interrupting at %s\n", devname, intrstr);
        /* Set LEGSUP register to its default value. */
        pci_conf_write(pc, tag, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN);

        switch(pci_conf_read(pc, tag, PCI_USBREV) & PCI_USBREV_MASK) {
                  ^^^^^^^ IRQ comes in here, during pci_conf_read()


Some playing around with timings reveals that the IRQ is caused by the 
write to PCI_LEGSUP.

Once the interrupt comes in, the handler tries to schedule a soft 
interrupt, but the soft interrupt information doesn't get set up until 
the generic usb code attaches, and so scheduling the soft interrupt 
dereferences an uninitialised pointer (in this case 0) which causes the 
uvm_fault.

At the time of the interrupt, the UHCI cmd register is 0xc1, the sts 
register is 0x03 and intr is 0x00. As far as I understand the spec, 0x03 
in sts represent interrupts which should be masked by intr being 0x00.

My reading of the UHCI spec doesn't give any hints as to why writing 
this register could cause an interrupt to be generated, so I can only 
assume that my understanding of the spec is wrong.

I tried writing 0x3f to the sts register right after where the 
interrupts are masked to clear pending interrupts, and that made the 
problem go away for me, but I'm reluctant to commit that without 
understanding why the interrupt was happening in the first place.

Can somebody with a better understanding of UHCI please comment?