PCI枚举黑客以数据中止异常结束

时间:2014-01-20 13:25:59

标签: linux arm u-boot pci

我正在开发一个arm-linux板,上面有几个PCI插槽。

我想检查UBoot中PCI模块的供应商ID /设备ID。 所以我将PCI驱动程序的初始化部分从linux移植到UBoot。

黑客: 由于我的主板上的PCI拓扑是固定的,我冒昧地在UBoot中硬编码总线编号(pirmary,secondary,subordinate),因此我不必将枚举代码移植到UBoot。为了获得总线编号,我编写了一个小的可加载内核模块,一旦内核完成枚举PCI总线上的设备,就可以获得 device 总线编号。

问题: 现在,如果模块存在于PCI插槽中,我可以成功读取其ID。 但是如果一个模块不存在并且我尝试读取它的ID,我会受到 ARM的数据中止处理程序的攻击。<​​/ p>

是否可以解决此数据中止异常,或者在尝试读取ID之前提前知道是否填充了插槽。


更新1: 我根据auselen的输入修改了UBoot源代码如下:

start.S中

//添加了以下宏

     .macro  irq_restore_user_regs_mod
     ldmia   sp, {r0 - lr}^                  @ Calling r0 - lr
     mov     r0, r0
     ldr     lr, [sp, #S_PC]                 @ Get PC
     add     sp, sp, #S_FRAME_SIZE
     mov     pc, lr                 @ return & move spsr_svc into cpsr
     .endm

更改了data_abort代码,如下所示

data_abort:
    get_bad_stack
    irq_save_user_regs
    bl  do_data_abort
    irq_restore_user_regs_mod

interrupts.c 将do_data_abort修改为

void do_data_abort (struct pt_regs *pt_regs)
{   
    if (flag == 1)
    {
        flag = 0;
        return;
    }
    printf ("data abort handler\n");
    printf ("Originally installed by U-Boot\n");
    show_regs (pt_regs);
    bad_mode ();
}

mypcie.c 尝试读取可能无效地址的代码部分

    printf("Trying possibly invalid address\n");
    flag = 1;
    data = *((volatile unsigned int *)(0xbe200000))  ;
    if (flag == 0) printf("Bad address \n");
    flag = 1;

UBoot日志的关注部分:

Trying possibly invalid address
data abort handler
Originally installed by U-Boot
pc : [<00012150>]    lr : [<00012144>]
sp : 46069a00  ip : 78000000  fp : 00000000
r10: 07f7eca4  r9 : 00000000  r8 : 07f7efdc
r7 : 00000000  r6 : 000000f8  r5 : 00000001  r4 : bb000000
r3 : be200000  r2 : 00020b28  r1 : 00000020  r0 : 07f7ea49
Flags: nzcv  IRQs on  FIQs on  Mode USER_32
U-Boot::Resetting CPU ...

我怀疑* irq_restore_user_regs_mod *正在将UBoot发送回* do_data_abort *。 因此,当do_data_abort执行标志为1时,do_data_abort将标志更改为0,irq_restore_user_regs_mod将UBoot发送回do_data_abort。由于标志为0,UBoot进入坏模式。

请告诉我是否应该使用

MOVS PC,LR

MOV PC,LR

irq_restore_user_regs_mod 中的

(代码段中的命令与文本不同)。

另请详细说明为何使用 MOV(S)PC,LR 而非 SUBS PC,LR,#4


更新2 :(根据auselen的评论)

i)将标志从简单的int更改为volatile ii)在 interrupts.c 中添加了printf,用于调试目的,如下所示:

printf("flag = %d\n",flag);
    if (flag == 1)
    {
        flag = 0;
        printf("FLAG = %d\n",flag);
        return;
    }

iii)在文件 mypcie.c

中,在数据中止访问之前和之后添加asm volatile("" ::: "memory");
flag = 1;
asm volatile("" ::: "memory");
data = *((volatile unsigned int *)(0xbe200000))  ;
asm volatile("" ::: "memory");
if (flag == 0) printf("Bad address \n");

结果

UBoot Log 1:

Trying possibly invalid address
flag = 1
FLAG = 0
flag = 1
FLAG = 0
(continues forever)

似乎控件一直返回到flag = 1; mypcie.c中的指令 如果我注释掉这个指令,并在此函数之外初始化标志为1,那么我得到以下日志:

UBoot Log 2:

Trying possibly invalid address
flag = 1
FLAG = 0
flag = 0
data abort handler
Originally installed by U-Boot
pc : [<00012174>]    lr : [<5306b01e>]
sp : c6a69a08  ip : 78000000  fp : 00000000
r10: 07f7eca1  r9 : 00000000  r8 : 07f7efdc
r7 : 00000000  r6 : 000000fb  r5 : 00000001  r4 : bb000000
r3 : be200000  r2 : 00000000  r1 : 00000020  r0 : 07f7ea4d
Flags: nzcv  IRQs on  FIQs on  Mode USER_32
U-Boot::Resetting CPU ...

现在看起来好像跟随指令执行了两次:

data = *((volatile unsigned int *)(0xbe200000))  ;

在第二次执行标志为0时,我们点击数据中止。


更新3 (根据auselen关于MOV,MOVS和SUBS的评论) 从UBoot目录中的config.mk文件中删除了-O2标志。

UBoot Logs

使用 subs pc,lr,#4

Trying possibly invalid address
flag = 1
FLAG = 0
prefetch abort handler
Originally installed by U-Boot
pc : [<90000004>]    lr : [<00012174>]
sp : 07f7eb80  ip : 78000000  fp : 00000000
r10: 00000000  r9 : 00000000  r8 : 07f7efdc
r7 : 00000000  r6 : 00000000  r5 : 00000000  r4 : 00008e00
r3 : 00000000  r2 : c6a68e1c  r1 : 00010001  r0 : 00000003
Flags: nZCv  IRQs on  FIQs on  Mode USER_32
U-Boot::Resetting CPU ...

使用 subs pc,lr,#8

Trying possibly invalid address
flag = 1
FLAG = 0
flag = 0
data abort handler
Originally installed by U-Boot
pc : [<00012174>]    lr : [<00008e7c>]
sp : c6a68cf4  ip : 78000000  fp : 00000000
r10: 07f7eca1  r9 : 00000000  r8 : 07f7efdc
r7 : 00000000  r6 : 000000fb  r5 : 00000001  r4 : bb000000
r3 : be200000  r2 : 00000000  r1 : 00000020  r0 : 07f7ea4d
Flags: nzcv  IRQs on  FIQs on  Mode USER_32
U-Boot::Resetting CPU ...

1 个答案:

答案 0 :(得分:1)

我没有尝试过这个我自己但是应该能够修改u-boot以在某些地址访问期间处理数据中止。

arch/arm/cpu/armv7/start.S包含

data_abort:
        get_bad_stack
        bad_save_user_regs
        bl      do_data_abort

似乎从代码中bad_save_user_regs需要使用irq_save_user_regs / irq_restore_user_regs*对更改,就像处理IRQ / FIQ一样。将data_abort读为

data_abort:
        get_bad_stack
        irq_save_user_regs
        bl      do_data_abort
        irq_restore_user_regs*

do_data_abort位于arch/arm/lib/interrupts.c

  void do_data_abort (struct pt_regs *pt_regs)
  {
          printf ("data abort\n\n    MAYBE you should read doc/README.arm-unaligned-accesses\n\n");
          show_regs (pt_regs);
          bad_mode ();
  } 

bad_mode重置cpu。

一种方法可能是在尝试可能的中止地址之前提高标志,然后在do_data_abort检查标志而不是bad_mode,如果是这种情况,则降低标志并继续下一条指令,应检查如果旗帜降低或不降低。

[*]可以使用subs PC, LR, #4的修改副本中的irq_restore_user_regs处理返回下一条指令。将其读作

          .macro  irq_restore_user_regs_mod
          ldmia   sp, {r0 - lr}^                  @ Calling r0 - lr
          mov     r0, r0
          ldr     lr, [sp, #S_PC]                 @ Get PC
          add     sp, sp, #S_FRAME_SIZE
          subs PC, LR, #4                         @ return & move spsr_svc into
                                                  @ cpsr
          .endm