Взлом перечисления 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
Другие вопросы по тегам