我正在开发一个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 ...
答案 0 :(得分:1)
我没有尝试过这个我自己但是应该能够修改u-boot以在某些地址访问期间处理数据中止。
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