捉虫日记 0012: insmod failed on RMI XLR (2)

来自Jack's Lab
跳转到: 导航, 搜索

根据前面的判断问题出在 PDC2xx IDE 驱动初始化时,于是一步步下去,加了一堆的跟踪 printk,Log 如下:

bus: 'pci': add driver Promise_IDE
[bus_add_driver: 662@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/bus.c]
[bus_add_driver: 679@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/bus.c]
[driver_attach: 300@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[bus_for_each_dev: 285@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/bus.c]
[bus_for_each_dev: 289@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/bus.c]
[bus_for_each_dev: 292@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/bus.c]
[__driver_attach: 271@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[__driver_attach: 274@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[__driver_attach: 276@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[driver_probe_device: 198@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[driver_probe_device: 201@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[driver_probe_device: 212@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[__driver_attach: 279@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[__driver_attach: 281@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[__driver_attach: 276@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[driver_probe_device: 198@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[driver_probe_device: 201@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
bus: 'pci': driver_probe_device: matched device 0000:00:01.0 with driver Promise_IDE
bus: 'pci': really_probe: probing driver Promise_IDE with device 0000:00:01.0
[really_probe: 110@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[really_probe: 118@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/base/dd.c]
[pdc202new_init_one: 838@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/pci/pdc202xx_new.c]
[pdc202new_init_one: 870@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/pci/pdc202xx_new.c]
[ide_pci_init_one: 548@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
556@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
[ide_pci_init_one: 563@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
[ide_pci_init_one: 570@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
pdc202xx_new 0000:00:01.0: PLL input clock is 16671 kHz
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
[ide_pci_init_one: 575@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
[ide_pci_init_one: 579@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/setup-pci.c]
[ide_host_register: 1643@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1655@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
ide2: DMA disabled
[ide_host_register: 1659@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1643@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1655@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
ide3: DMA disabled
[ide_host_register: 1659@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1666@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1673@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
Probing IDE interface ide2...
hde: Maxtor 6L200P0, ATA DISK drive
[ide_host_register: 1677@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1682@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
hde: host max PIO4 wanted PIO255(auto-tune) selected PIO4
[ide_host_register: 1685@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
[ide_host_register: 1673@/ubuntu/src/wrs.git/workspace/rmi_new/build/linux/drivers/ide/ide-probe.c]
Probing IDE interface ide3...
CPU 0 Unable to handle kernel paging request at virtual address 0000000000001ff8, epc == ffffffffc0001c20, ra == ffffffff80000008
Oops[#1]:
Cpu 0
$ 0   : 0000000000000000 0000000000000014 ffffffffc0001c20 ffffffffc0002038
$ 4   : a8000001273423c8 a800000123db3830 0000000000000000 ffffffffffff00fe
$ 8   : ffffffff83928020 0000000000000000 0000000000000000 0000000000000001
$12   : 0000000000000028 ffffffff834056e8 0000000000000000 ffffffff83898000
$16   : a8000001273423c8 00000000000000a1 a800000127342000 ffffffffc0002038
$20   : 0000000000000001 0000000000000040 0000000000000010 7ffffffffffff8f0
$24   : 0000000000000000 ffffffff83669bf8                                 
$28   : a800000123db0000 a800000123db3830 ffffffffffffffed ffffffff80000008
Hi    : 0000000000000000
Lo    : 0000000000000000
epc   : ffffffffc0001c20 phnx_tf_load+0x0/0x250 [pdc202xx_new]
    Not tainted
ra    : ffffffff80000008 0xffffffff80000008
Status: 1000dce3    KX SX UX KERNEL EXL IE
Cause : 00800008
BadVA : 0000000000001ff8
PrId : 000c0b04 (RMI Phoenix)
Modules linked in: pdc202xx_new(+)
Process insmod (pid: 2752, threadinfo=a800000123db0000, task=a800000124c03208, tls=00000000004a1470)
Stack : 0000000000000000 0000000000000000 0000400000000000 0000000000000000
        0000000000000000 ffffffff83461158 a8000001273423c8 ffffffff836810b8
        0000000000000000 0000000008000000 2000000000000000 0000000000000000
        0000000000000000 ffffffff83681550 0000000000000001 a800000127342330
        a800000127342330 a8000001273423c8 a800000127342000 0000000000000001
        0000000000000040 ffffffff83681704 0000000000000018 000000001000dce1
        a800000127342000 0000000000000008 0000000000000050 a800000124d79ad8
        a80000012612b7b0 ffffffff83865840 ffffffff8385e010 ffffffff837fc608
        ffffffff83800000 ffffffff83682ed0 a80000012612b028 ffffffffc0002148
        0000000000000001 a800000123db39b0 0000000000000018 a800000126c71278
        ...
Call Trace:
[<ffffffffc0001c20>] phnx_tf_load+0x0/0x250 [pdc202xx_new]
[<ffffffff80000008>] 0xffffffff80000008

Code: 8e030010 080006a9 a2020001 <67bdffd0> ffb30020 ffb20018 ffb00008 ffbf0028 ffb10010

多次尝试,一会在 phnx_tf_load() 里访问非法 VA,一会又在 phnx_set_irq() 中访问非法 VA,一会又在其他地方,但在 phnx_tf_load 和 phnx_set_irq 出现的概率最高,遂又仔细看了这两个函数的实现,并没发现有显著的失误处。郁闷至极。

附 IDE 正常启动 Log:

Uniform Multi-Platform E-IDE driver
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
pdc202xx_new 0000:00:01.0: PLL input clock is 16667 kHz
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
ide2: DMA disabled
ide3: DMA disabled
hde: Maxtor 6L200P0, ATA DISK drive
ide2 at 0x10000000-0x10000007,0x1000000a on irq 24
isa bounce pool size: 16 pages
ide3 at 0x10000010-0x10000017,0x1000001a on irq 24
hde: max request size: 512KiB
hde: 398297088 sectors (203928 MB) w/8192KiB Cache, CHS=24792/255/63
hde: cache flushes supported
hde: hde1
......

2009.02.17 11:21



突然间想起 Cunbo 在 fix 这个 BSP 串口问题时,将 __raw_readb/writeb 改成 __raw_readl/writel 就 OK 了,很自然地联想起不知在什么地方看过 raza 上访问内存映射的 I/O 寄存器有一些对齐限制(事后证明是想当然)于是将用于 PCI I/O 读写的函数 rmi_ide_mm_outb() 的实现由 __raw_writeb() 改成了 __raw_writel() 一试居然可以 insmod 了。其原始实现为:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    __raw_writeb(value, pci_ide_phys_to_virt(port));
}

总算找到 root cause 了,只是结论来得有点迟。上午都有些失去耐心了,还是要 orz。

2009.02.17 13:21



乐极生悲了,不是 root casue,把 __raw_writeb 改成 __raw_writel 后 insmod 是可以了,硬盘却发现不了了,下午激动过头了,就没注意。

奇怪的是为什么把字节写改成字 (32bit) 写,硬盘就发现不了了。

这么一改,IDE 驱动就没真正工作,故而问题也就不出现了。

2009.02.17 22:43



冷静了下,发现 IDE 驱动没法正常工作的原因在于:

RMI XLR 下 __raw_writeb/l 函数定义为:

#define __raw_writeb(v,a)       (*(volatile unsigned char *)(a) = (v))
#define __raw_writel(v,a)       (*(volatile unsigned int   *)(a) = (v))

比如原来是用 __raw_writeb(value, pci_ide_phys_to_virt(port)) 向地址 0x9000 0000 1000 0004 处写一个字节 直接改成 __raw_writel(value, pci_ide_phys_to_virt(port)) 后,是向同样的地址处写一个字,因为 value 是 u8 类型,*(volatile unsigned int *)(a) = (v) 在大端上等于是将 value 写到 0x9000 0000 1000 0007 处了,原 0x9000 0000 1000 0004 处等于是写了个 0x00。也就是向 IDE 的 I/O 寄存器写错了值,发现不了硬盘也就自然了。


明白了这一点,顺手就改成了:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    __raw_writel(swab32((u32)value), pci_ide_phys_to_virt(port));
}


这下就更 COOL 了:

......
Uniform Multi-Platform E-IDE driver
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
pdc202xx_new 0000:00:01.0: PLL input clock is 16597 kHz
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
ide2: DMA disabled
ide3: DMA disabled
*********************************************
cpu_0 received a bus/cache error
*********************************************
Bridge: Phys Addr = 0x0000000000, Device_AERR = 0x00000000
Bridge: The devices reporting AERR are:
CPU: (XLR specific) Cache Error log = 0x0080000800000601, Phy Addr = 0x0010000008
CPU: epc = 0xffffffff836fcb68, errorepc = 0xffffffff8342938c, cacheerr = 0x00000000
Can not handle bus/cache error - Halting cpu


看到这个 bus/cache error 是比较郁闷的,当初移植是,似乎在哪里看到对 RMI XLR 上的 bus/cache error 有个注释,找了下,确实有这么个注释:

/*
* On Phoenix, errors reported by bridge (like misconfigured BARS etc) are
* also reported as cache errors. Need to check if it is really a cache
* error or a "bus error" and take action appropriately.
* For now, treat it as a cache error
*/

对照内核对 cache error 异常的处理代码,又翻了下文档,对 Cache Error log 和 Phy Addr 进行了解析,实际上就是访问 0x0010000008 这个物理地址导致的。Cache Error log 的内容来自于 L1D_CACHE_ERROR_LOG 这个寄存器,细看了,没有记录太有价值的信息。

代码里还有段注释:

    /*
     * TODO: cache error on xlr could also be because of access to a physical
     * address region that is not mapped to any device in the bridge
     * We should detect that condition by reading the bridge registers and if
     * a process is doing the access, the process should be SEGFAULTED. If
     * kernel is doing it, all bets are off, so we should dump the stack and
     * die. For Now, just halt the cpu
    */

百思不得其解,0x10000008 这个 PA 怎么就没有被映射。

2009.02.17 23:53



接昨天的分析,继续 rmi_ide_mm_outb() 的探索,怀疑是不是向相邻的 IDE I/O register 写了 0 值导致的 bus/cache error,于是:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    u32 tmp = *(u32 *)(pci_ide_phys_to_virt(port));
    tmp &= 0x00ffffff;
    __raw_writel(tmp | swab32((u32)value), pci_ide_phys_to_virt(port));
}


不太放心 toolchain,把指令 dump 出来看了下:

0000000000000000 <rmi_ide_mm_outb>:
   0:   3c020000    lui v0,0x0
   4:   dc470000    ld a3,0(v0)
   8:   3c030000    lui v1,0x0
   c:   dc660000    ld a2,0(v1)
10:   00a7282f    dsubu   a1,a1,a3
14:   00c5302d    daddu   a2,a2,a1
18:   8cc30000    lw v1,0(a2)
1c:   3c0200ff    lui v0,0xff
20:   308400ff    andi    a0,a0,0xff
24:   3442ffff    ori v0,v0,0xffff
28:   00621824    and v1,v1,v0
2c:   00042600    sll a0,a0,0x18
30:   00641825    or v1,v1,a0
34:   acc30000    sw v1,0(a2)
38:   03e00008    jr ra
3c:   00000000    nop


生成的指令没问题,一试,同样的现象:

Uniform Multi-Platform E-IDE driver
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
pdc202xx_new 0000:00:01.0: PLL input clock is 16597 kHz
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
ide2: DMA disabled
ide3: DMA disabled
*********************************************
cpu_0 received a bus/cache error
*********************************************
Bridge: Phys Addr = 0x0000000000, Device_AERR = 0x00000000
Bridge: The devices reporting AERR are:
CPU: (XLR specific) Cache Error log = 0x0080000800000601, Phy Addr = 0x0010000008
CPU: epc = 0xffffffff836fcb68, errorepc = 0xffffffff8342938c, cacheerr = 0x00000000
Can not handle bus/cache error - Halting cpu


不甘心,又试了个 64 bit 的:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    uint64_t tmp = *(uint64_t *)(pci_ide_phys_to_virt(port));
    tmp &= 0x00ffffffffffffff;
    *(uint64_t *)(pci_ide_phys_to_virt(port)) = tmp | swab64((uint64_t)value);
    tmp = *(uint64_t *)(pci_ide_phys_to_virt(port));
}

ffffffff836fcb50 <rmi_ide_mm_outb>:
ffffffff836fcb50:   3c02838e    lui v0,0x838e
ffffffff836fcb54:   dc473360    ld a3,13152(v0)
ffffffff836fcb58:   3c038399    lui v1,0x8399
ffffffff836fcb5c:   dc662eb0    ld a2,11952(v1)
ffffffff836fcb60:   00a7282f    dsubu   a1,a1,a3
ffffffff836fcb64:   00c5302d    daddu   a2,a2,a1
ffffffff836fcb68:   24020001    li v0,1
ffffffff836fcb6c:   dcc30000    ld v1,0(a2)
ffffffff836fcb70:   308400ff    andi    a0,a0,0xff
ffffffff836fcb74:   0002163c    dsll32 v0,v0,0x18
ffffffff836fcb78:   00042600    sll a0,a0,0x18
ffffffff836fcb7c:   6442ffff    daddiu v0,v0,-1
ffffffff836fcb80:   00621824    and v1,v1,v0
ffffffff836fcb84:   0004203c    dsll32 a0,a0,0x0
ffffffff836fcb88:   00641825    or v1,v1,a0
ffffffff836fcb8c:   03e00008    jr ra
ffffffff836fcb90:   fcc30000    sd v1,0(a2)
ffffffff836fcb94:   00000000    nop

Uniform Multi-Platform E-IDE driver
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
*********************************************
cpu_0 received a bus/cache error
*********************************************
Bridge: Phys Addr = 0x0000000000, Device_AERR = 0x00000000
Bridge: The devices reporting AERR are:
CPU: (XLR specific) Cache Error log = 0x0080000800001a01, Phy Addr = 0x0010000030
CPU: epc = 0xffffffff836fcb6c, errorepc = 0xffffffff8342938c, cacheerr = 0x00000000
Can not handle bus/cache error - Halting cpu

2009.02.18 10:23



都没什么感觉了,随手就在 rmi_ide_mm_outb() 里把每次进来的地址打了出来:

root@rmi_atx_2-1:/root> insmod ./pdc202xx_new.ko
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
ide_addr = 0x10000021
ide_addr = 0x10000029
ide_addr = 0x10000021
ide_addr = 0x10000021
ide_addr = 0x10000029
ide_addr = 0x10000029
ide_addr = 0x10000021
ide_addr = 0x10000023
pdc202xx_new 0000:00:01.0: PLL input clock is 13911 kHz
ide_addr = 0x10000029
ide_addr = 0x1000002b
ide_addr = 0x10000029
ide_addr = 0x1000002b
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
ide2: DMA disabled
ide3: DMA disabled
ide_addr = 0x10000006
*********************************************
cpu_16 received a bus/cache error
*********************************************
Bridge: Phys Addr = 0x0128000000, Device_AERR = 0x00000004
Bridge: The devices reporting AERR are:
    cpu 2
CPU: (XLR specific) Cache Error log = 0x0000000800000601, Phy Addr = 0x0010000008
CPU: epc = 0xffffffff836fb0e4, errorepc = 0xffffffff83428c3c, cacheerr = 0x00000000
Can not handle bus/cache error - Halting cpu


看到是访问了 0x10000006 这个地方导致的,揣测这个地址不对:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    printk("<0> ide_addr = 0x%016lx\n", pci_ide_phys_to_virt(port));
    if(pci_ide_phys_to_virt(port) < 0x9000000010000020)
    {
        dump_stack();
        return;
    }
    __flush_cache_all();
    u32 tmp = *(u32 *)(pci_ide_phys_to_virt(port));
    tmp &= 0x00ffffff;
    __raw_writel(tmp | swab32((u32)value), pci_ide_phys_to_virt(port));
}
pdc202xx_new 0000:00:01.0: IDE controller (0x105a:0x4d69 rev 0x02)
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000029
ide_addr = 0x9000000010000021
ide_addr = 0x9000000010000023
pdc202xx_new 0000:00:01.0: PLL input clock is 14018 kHz
ide_addr = 0x9000000010000029
ide_addr = 0x900000001000002b
ide_addr = 0x9000000010000029
ide_addr = 0x900000001000002b
pdc202xx_new 0000:00:01.0: 100% native mode on irq 24
ide2: DMA disabled
ide3: DMA disabled
ide_addr = 0x9000000010000006
Call Trace:
[<ffffffff8340bad0>] dump_stack+0x8/0x38
[<ffffffff8367ee18>] SELECT_DRIVE+0x60/0x70
[<ffffffff83683198>] ide_probe_port+0x148/0x740
[<ffffffff83684880>] ide_host_register+0x290/0x728
[<ffffffff83687554>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105b8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231d8>] kernel_thread_helper+0x10/0x18

ide_addr = 0x9000000010000006
Call Trace:
[<ffffffff8340bad0>] dump_stack+0x8/0x38
[<ffffffff8367ee18>] SELECT_DRIVE+0x60/0x70
[<ffffffff83683198>] ide_probe_port+0x148/0x740
[<ffffffff83684880>] ide_host_register+0x290/0x728
[<ffffffff83687554>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105b8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231d8>] kernel_thread_helper+0x10/0x18

ide_addr = 0x900000001000000a
Call Trace:
[<ffffffff8340bad0>] dump_stack+0x8/0x38
[<ffffffff836831ac>] ide_probe_port+0x15c/0x740
[<ffffffff83684880>] ide_host_register+0x290/0x728
[<ffffffff83687554>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105b8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231d8>] kernel_thread_helper+0x10/0x18

ide_addr = 0x900000001000001a
Call Trace:
[<ffffffff8340bad0>] dump_stack+0x8/0x38
[<ffffffff836831ac>] ide_probe_port+0x15c/0x740
[<ffffffff83684880>] ide_host_register+0x290/0x728
[<ffffffff83687554>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105b8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231d8>] kernel_thread_helper+0x10/0x18

ide_addr = 0x9000000010000016
Call Trace:
[<ffffffff8340bad0>] dump_stack+0x8/0x38
[<ffffffff8367ee18>] SELECT_DRIVE+0x60/0x70
[<ffffffff836831fc>] ide_probe_port+0x1ac/0x740
[<ffffffff83684880>] ide_host_register+0x290/0x728
[<ffffffff83687554>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105b8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231d8>] kernel_thread_helper+0x10/0x18


可以看到多次访问了低于 0x10000020 的地址,揣测失败。不知受了什么刺激,突然想起 bootloader 打出的该 IDE 控制器的 PCI I/O 段:

[pci {0x4d69:0x105a} {0x00:0x01:0x00}]
    BAR[0] 0x10000000:0x10000008 IO
    BAR[1] 0x10000008:0x1000000c IO
    BAR[2] 0x10000010:0x10000018 IO
    BAR[3] 0x10000018:0x1000001c IO
    BAR[4] 0x10000020:0x10000030 IO
    BAR[5] 0xd0000000:0xd0004000 Memory
    BAR[6] 0xd0004000:0xd0008000 ROM


内核里的则为:

root@rmi_atx_2-1:/root> cat /proc/ioports
10000000-100fffff : PHOENIX IO MEM
10000000-10000007 : pdc202xx_new
10000008-1000000b : pdc202xx_new
10000010-10000017 : pdc202xx_new
10000018-1000001b : pdc202xx_new
10000020-1000002f : pdc202xx_new
10000100-100001ff : 8139too

因此 lw 0x900000001000000a 时就会超出 PA (BAR[1]),访问到没有映射的区域去了

所以在这用 ld/lw 是不行的,肯定会出现 cache/bus error

总算可以解释了一个一直让我头疼不已的现象,小高兴下。

2009.02.18 11:13



中午出去小转了下,刚下的雪差不多都化了,盼了这么久的雪,走得太快。回来百无聊赖,继续盯 rmi_ide_mm_outb(),试了几次,突然就注意到这么一个 Log:

root@rmi_atx_2-1:/root> insmod ./pdc202xx_new.ko
ide_vaddr = 0x9000000010000021
ide_vaddr = 0x9000000010000021
ide_vaddr = 0x9000000010000029
ide_vaddr = 0x900000001000002b
ide_vaddr = 0x9000000010000006
ide_vaddr = 0x900000001000000a
ide_vaddr = 0x9000000010000006
ide_vaddr = 0x900000001000000a
ide_vaddr = 0x9000000010000006
ide_vaddr = 0x9000000010000006
ide_vaddr = 0x900000001000000a
ide_vaddr = 0x900000001000000a
ide_vaddr = 0x9000000010000001
ide_vaddr = 0x9000000010000007
ide_vaddr = 0x900000001000000a
ide_vaddr = 0x9000000010000001
ide_vaddr = 0x9000000010000002
ide_vaddr = 0x9000000010000007
ide_vaddr = 0x9000000000000001
ide_vaddr = 0x9000000000000003
ide_vaddr = 0x9000000000000001
ide_vaddr = 0x9000000000000003
ide_vaddr = 0x9000000000000001
ide_vaddr = 0x9000000000000003
ide_vaddr = 0x9000000010000016
ide_vaddr = 0x900000001000001a
ide_vaddr = 0x9000000010000016
ide_vaddr = 0x900000001000001a
ide_vaddr = 0x9000000010000016
ide_vaddr = 0x900000001000001a
ide_vaddr = 0x9000000010000016


出现这么一堆很怪的地址,这个地址映射到物理地址 0x1 和 0x3 处,这个地方正是 TLB refill handler 之所在,写这两个字节,等于就是改写了 TLB refill handler!

一下子,当初的 workaround 是怎么可以的,为什么 insmod 会失败全清晰了,这个感觉太美妙了。

是越来越接近 BOSS 了。

2009.02.18 16:03



似乎搞定了这个问题,Log 之

难道是出去晃悠了半天,与雪合一,境界提升了 :-)

2009.02.19 17:17



很自然的把 rmi_ide_mm_outb() 改为:

void rmi_ide_mm_outb (u8 value, unsigned long port)
{
    printk("<0> port = 0x%016lx\n", port);
    if(port < 0x10000000ULL)
    {  
        printk("<0> illigal ide_vaddr = 0x%lx\n", pci_ide_phys_to_virt(port));
        dump_stack();
    }
    printk("<0> ide_vaddr = 0x%lx\n", pci_ide_phys_to_virt(port));
    *(u8 *)(pci_ide_phys_to_virt(port)) = value;
}  


即当检测到写的地址不正常时,直接 dump_stack() ,这样就可以很容易定位是谁要写这么个地址的:

port = 0x0000000000000001
illigal ide_vaddr = 0x9000000000000001
Call Trace:
[<ffffffff8340baf0>] dump_stack+0x8/0x38
[<ffffffff836fcfbc>] rmi_ide_mm_outb+0xb4/0xc0
[<ffffffff8368a6e8>] set_indexed_reg+0x50/0x78
[<ffffffff8368a7bc>] pdcnew_set_pio_mode+0xac/0xe0
[<ffffffff83681ea8>] ide_set_pio_mode+0xe0/0xe8
[<ffffffff83683954>] ide_port_tune_devices+0x1a4/0x1c8
[<ffffffff8368493c>] ide_host_register+0x32c/0x770
[<ffffffff836875bc>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105d8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231f8>] kernel_thread_helper+0x10/0x18

ide_vaddr = 0x9000000000000001


port = 0x0000000000000003
illigal ide_vaddr = 0x9000000000000003
Call Trace:
[<ffffffff8340baf0>] dump_stack+0x8/0x38
[<ffffffff836fcfbc>] rmi_ide_mm_outb+0xb4/0xc0
[<ffffffff8368a7bc>] pdcnew_set_pio_mode+0xac/0xe0
[<ffffffff83681ea8>] ide_set_pio_mode+0xe0/0xe8
[<ffffffff83683954>] ide_port_tune_devices+0x1a4/0x1c8
[<ffffffff8368493c>] ide_host_register+0x32c/0x770
[<ffffffff836875bc>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105d8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231f8>] kernel_thread_helper+0x10/0x18

ide_vaddr = 0x9000000000000003


可以看到主要是 IDE controller driver 初始化时,不知为什么在 set_indexed_reg() 和 pdcnew_set_pio_mode() 往物理地址 0x1 和 0x3 处各写了一个字节。

set_indexed_reg() 实现为:

static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value)
{
    rmi_ide_mm_outb(index, hwif->dma_base + 1);
    rmi_ide_mm_outb(value, hwif->dma_base + 3);
    DBG("index[%02X] value[%02X]\n", index, value);
}


即其直接向 hwif->dma_base + 1 和 hwif->dma_base+3 处各写了两个字节,反推 hwif->dma_base 为 0,这个很容易验证的:

static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value)
{
    printk("<0> set_indexed_reg: hwif->name = %s\n", hwif->name);
    printk("<0> hwif->dma_base = 0x%p\n", hwif->dma_base);
    rmi_ide_mm_outb(index, hwif->dma_base + 1);
    rmi_ide_mm_outb(value, hwif->dma_base + 3);
    DBG("index[%02X] value[%02X]\n", index, value);
}


Log 就变为:

set_indexed_reg: hwif->name = ide2
hwif->dma_base = 0x0000000000000000
port = 0x0000000000000001
illigal ide_vaddr = 0x9000000000000001
Call Trace:
[<ffffffff8340baf0>] dump_stack+0x8/0x38
[<ffffffff836fcfbc>] rmi_ide_mm_outb+0xb4/0xc0
[<ffffffff8368a6e8>] set_indexed_reg+0x50/0x78
[<ffffffff8368a7bc>] pdcnew_set_pio_mode+0xac/0xe0
[<ffffffff83681ea8>] ide_set_pio_mode+0xe0/0xe8
[<ffffffff83683954>] ide_port_tune_devices+0x1a4/0x1c8
[<ffffffff8368493c>] ide_host_register+0x32c/0x770
[<ffffffff836875bc>] ide_pci_init_one+0xdc/0x100
[<ffffffff83910dac>] ide_scan_pcibus+0x98/0x17c
[<ffffffff834105d8>] __kprobes_text_end+0x38/0x180
[<ffffffff838f8378>] kernel_init+0x250/0x2cc
[<ffffffff834231f8>] kernel_thread_helper+0x10/0x18

ide_vaddr = 0x9000000000000001


很显然 dma_base 肯定不能为 0 的,于是遍寻 drivers/ide/ 下的初始化代码 grep -r "hwif->dma_base\ =",很容易就定位到 ide_init_port():

        if (d->init_dma)
            rc = d->init_dma(hwif, d);
        else
            rc = ide_hwif_setup_dma(hwif, d);

在驱动未提供 init_dma 函数时,其使用 ide_hwif_setup_dma(),而这个函数实现为:

int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
{
    struct pci_dev *dev = to_pci_dev(hwif->dev);

    if ((d->host_flags & IDE_HFLAG_NO_AUTODMA) == 0 ||
        ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
         (dev->class & 0x80))) {
        unsigned long base = ide_pci_dma_base(hwif, d);
   
        if (base == 0)
            return -1;

        hwif->dma_base = base;

        if (ide_pci_check_simplex(hwif, d) < 0)
            return -1;

        if (ide_pci_set_master(dev, d->name) < 0)
            return -1;

        if (hwif->host_flags & IDE_HFLAG_MMIO)
            printk(KERN_INFO "    %s: MMIO-DMA\n", hwif->name);
        else
            printk(KERN_INFO "    %s: BM-DMA at 0x%04lx-0x%04lx\n",
                     hwif->name, base, base + 7);
   
        hwif->extra_base = base + (hwif->channel ? 8 : 16);

        if (ide_allocate_dma_engine(hwif))
            return -1;

        hwif->dma_ops = &sff_dma_ops;
    }

    return 0;
}

unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d)
{
    struct pci_dev *dev = to_pci_dev(hwif->dev);
    unsigned long dma_base = 0;

    if (hwif->host_flags & IDE_HFLAG_MMIO)
        return hwif->dma_base;

    if (hwif->mate && hwif->mate->dma_base) {
        dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
    } else {
        u8 baridx = (d->host_flags & IDE_HFLAG_CS5520) ? 2 : 4;

        dma_base = pci_resource_start(dev, baridx);

        if (dma_base == 0) {
            printk(KERN_ERR "%s %s: DMA base is invalid\n",
                d->name, pci_name(dev));
            return 0;
        }
    }

    if (hwif->channel)
        dma_base += 8;

    return dma_base;
}

因为驱动加了 IDE_HFLAG_MMIO 标志,故而其没有真正地为其计算 dma_base,直接返回就为 0 了!

2009.02.20 22:58 补记




Solution

原因找到,解决就很容易了,要不实现一个简单的 init_dma(),要不将其条件编译也可:

#ifdef CONFIG_CPU_PHOENIX
    if (hwif->host_flags & IDE_HFLAG_MMIO)
        return hwif->dma_base;
#endif

















个人工具
名字空间

变换
操作
导航
工具箱