Взлом перечисления PCI заканчивается исключением исключения данных
Я работаю над платой arm-linux, на которой есть пара слотов PCI.
Я хотел проверить идентификаторы поставщиков / идентификаторов устройств PCI-модулей в UBoot. Поэтому я перенес часть инициализации драйвера PCI из linux в UBoot.
Хак: Поскольку топология PCI на моей плате фиксирована, я взял на себя смелость жестко кодировать номера шин (первичные, вторичные, подчиненные) в UBoot, поэтому мне не нужно переносить код перечисления в UBoot. Чтобы получить номера шин, я написал небольшой загружаемый модуль ядра, который дает мне номера шин устройств после того, как ядро выполнит перечисление устройств на шине PCI.
Проблема: теперь, если модули присутствуют в слотах PCI, я могу успешно прочитать их идентификаторы. Но если модуль отсутствует, и я пытаюсь прочитать его идентификаторы, меня сбивает обработчик прерывания данных ARM.
Есть ли способ обойти это исключение прерывания данных или заранее узнать, заполнен ли слот или нет, прежде чем пытаться прочитать идентификаторы.
Обновление 1: я изменил источник UBoot в соответствии с вводом auselen следующим образом:
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*. Таким образом, 1-й раз, когда 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) Добавлены printf(s) в interrupts.c для целей отладки следующим образом:
printf("flag = %d\n",flag);
if (flag == 1)
{
flag = 0;
printf("FLAG = %d\n",flag);
return;
}
iii) Добавлено asm volatile("" ::: "memory");
до и после прерывания доступа к данным, в файле mypcie.c
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) убрал флаг -O2 из файла config.mk в каталоге UBoot.
UBoot Logs
Используя sub 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 ...
Используя sub 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 ответ
Я не пробовал это сам, но можно изменить u-boot для обработки прерывания данных во время определенных обращений к адресу.
арка / рука / процессор / 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
сбрасывает процессор.
Один из подходов может заключаться в том, чтобы поднять флаг, прежде чем пытаться выполнить возможную отмену адреса, затем 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