Неопределенная инструкция исключение в коде ARM - PullRequest
1 голос
/ 28 мая 2019

Я занимаюсь программированием с нуля (разрабатываю ядро) на ARM-Cortex A53, SoC BCM2837 (Raspberry PI 3, другими словами). На самом деле я пишу программный продукт, отвечающий за работу с мини-UART (своего рода привет, как сообщается на OsDev wiki https://wiki.osdev.org/ARM_RaspberryPi_Tutorial_C).). Итак, я написал набор функций для работы с мини-UART, давайте рассмотрим следующее, поскольку проблема сохраняется для любой из других функций:

void miniUartSendByte(unsigned char byte){
  // FIFO can accept at least one byte
  while(*AUX_MU_LSR_REG & 0b100000);

  // write byte to buffer
  *AUX_MU_IO_REG = byte;
  return;
}

где AUX_MU_ * имеет тип volatile unsigned int *. это дизассемблирование кода выше:

  1000ac:       d10043ff        sub     sp, sp, #0x10
  1000b0:       39003fe0        strb    w0, [sp, #15]
  1000b4:       d503201f        nop
  1000b8:       d28a0a80        mov     x0, #0x5054                     // #20564
  1000bc:       f2afc420        movk    x0, #0x7e21, lsl #16
  1000c0:       b9400000        ldr     w0, [x0]
  1000c4:       121b0000        and     w0, w0, #0x20
  1000c8:       7100001f        cmp     w0, #0x0
  1000cc:       54ffff61        b.ne    1000b8 <miniUartSendByte+0xc>   // b.any
  1000d0:       d28a0800        mov     x0, #0x5040                     // #20544
  1000d4:       f2afc420        movk    x0, #0x7e21, lsl #16
  1000d8:       39403fe1        ldrb    w1, [sp, #15]
  1000dc:       b9000001        str     w1, [x0]
  1000e0:       d503201f        nop
  1000e4:       910043ff        add     sp, sp, #0x10
  1000e8:       d65f03c0        ret

и это выполнение в соответствии с сообщением QEMU:

----------------
IN: kernel_main
0x00100050:  a9bf7bfd  stp      x29, x30, [sp, #-0x10]!
0x00100054:  910003fd  mov      x29, sp
0x00100058:  52800c60  movz     w0, #0x63
0x0010005c:  94000012  bl       #0x1000a4 // jump to miniUartSendByte

----------------
IN: miniUartSendByte
0x001000a4:  d10043ff  sub      sp, sp, #0x10
0x001000a8:  39003fe0  strb     w0, [sp, #0xf]
0x001000ac:  d503201f  nop      
0x001000b0:  d28a0a80  movz     x0, #0x5054
0x001000b4:  f2afc420  movk     x0, #0x7e21, lsl #16
0x001000b8:  b9400000  ldr      w0, [x0]
0x001000bc:  121b0000  and      w0, w0, #0x20
0x001000c0:  7100001f  cmp      w0, #0
0x001000c4:  54ffff61  b.ne     #0x1000b0

----------------
IN: 
0x00000200:  00000000  .byte    0x00, 0x00, 0x00, 0x00 // ??

Как вы можете видеть, когда машина выполняет переход, она получает исключение и переходит на адрес 0x200, где находится обработчик прерываний (обратите внимание, что обработчик прерываний не был настроен, я еще не реализовал его) и застревает по адресу 0x200, выполняя бесконечный цикл (поведение по умолчанию, когда отсутствует обработчик прерываний). Теперь из QEMU мне удалось зафиксировать тип исключения:

Taking exception 1 [Undefined Instruction]
...from EL3 to EL3
...with ESR 0x0/0x2000000
...with ELR 0x200
...to EL3 PC 0x200 PSTATE 0x3cd

Я компилирую с помощью следующей команды:

aarch64-elf-gcc -Wall -O0 -ffreestanding -nostdinc -nostdlib -nostartfiles -mcpu=cortex-a53 -g -c ... -o ...

Я также пытался определить, была ли это проблема «компилятора», пытаясь выполнить следующий совершенно бесполезный код:

void a(){
  for(int j=0; j<10; j++);
  return;
}

void b(char* string){
  for(int i = 0; i<10; i++){
    a();
  }
  return;
}

void kernel_main(){

  a();
  b("test");

  while(1);

  return;
}

но исполнение идет без проблем ... Теперь я не могу понять, что происходит не так. Я имею в виду, что нет ничего плохого в коде C, производящем этот ассемблер, и адреса в ассемблерном коде кажутся нормальными ... любая идея, где возникает проблема ?? Почему это неопределенное исключение в инструкции ?? Если потребуется дополнительная информация, я могу предоставить более подробную информацию

1 Ответ

0 голосов
/ 28 мая 2019

Проблема была в неправильном базовом адресе, установленном для доступа к регистрам периферийных устройств. После установки значения 0x3F000000 исключений больше не возникает.

Я установил в качестве базового адреса 0x7E000000, вводя в заблуждение то, что сообщалось в спецификации BCM:

Физические адреса для периферийных устройств варьируются от 0x3F000000 до 0x3FFFFFFF. Адреса шины для периферийных устройств настроены для сопоставления с диапазоном адресов периферийной шины. начиная с 0x7E000000. Таким образом, периферийное устройство, рекламируемое здесь по адресу шины 0x7Ennnnnn, является доступно по физическому адресу 0x3Fnnnnnn.

Но позже сообщается:

Адреса периферийных устройств, указанные в этом документе, являются адресами шины. Программное обеспечение напрямую доступ к периферии должен переводить эти адреса в физические или виртуальные адреса

...