arm 32-битная инструкция "swi" на 64-битном процессоре - PullRequest
0 голосов
/ 08 сентября 2018

Я делаю свое ядро ​​на Raspberry Pi 3 (без Bluetooth). Мое ядро ​​использует язык ассемблера arm (32bit), а c и uboot загружают мое ядро.

Я нашел таблицу векторов прерываний и применил ее к своему коду следующим образом.

.globl _ram_entry
_ram_entry:
    bl  kernel_init 
    b   _ram_entry // 
    ldr pc,=print_mem1
    b   print_mem1
    b   print_mem1
    b   print_mem2
    b   print_mem3
    b   print_mem4
    b   print_mem1
    b   print_mem2
    b   print_mem3
    b   print_mem4
#define svc_stack 0xa0300000
#define irq_stack 0xa0380000
#define sys_stack 0xa0400000

.global kernel_init
kernel_init:
    ldr r0,=0x00080008
    mov r1,#0x0000
    ldmia r0!,{r2,r3,r4,r5}
    stmia r1!,{r2,r3,r4,r5}
    ldmia r0!,{r2,r3,r4,r5}
    stmia r1!,{r2,r3,r4,r5}
    ldmia r0!,{r2,r3,r4,r5}
    stmia r1!,{r2,r3,r4,r5}
    ldmia r0!,{r2,r3,r4,r5}
    stmia r1!,{r2,r3,r4,r5}

    bl  main
    b   _ram_entry

.global print_mem1
print_mem1:
    bl  print_c_mem1    

.global print_mem2
print_mem2:
    bl  print_c_mem2

.global print_mem3
print_mem3:
    bl  print_c_mem3

.global print_mem4
print_mem4:
    bl  print_c_mem4

_ram_entry начинается с 0x00080008, который является моей таблицей векторов прерываний. Когда я печатаю свою память, 0x00 содержит bl kernel_init. Все обработчики прерываний просто печатают простое число.

Но если я использую swi, как этот основной код, вызывается обработчик сброса.

int main()
{
    R_GPIO_REGS * gp_regs= (R_GPIO_REGS*)GPIO_BASE_ADDRESS;
    gp_regs->GPFSEL[1] =0x1000000;

    uart_init();

    printf("hellow world\n");
    vector_memory_dump();
    unsigned int destrst=0xea020000;
    unsigned int destirq=0xea020000;
    unsigned int destswi=0xea020000;
    PUT32(MEMZERO,destrst);
    PUT32(MEMY,destirq);
    PUT32(MEMSWI,destswi);
    vector_memory_dump();
    //asm("b 0x04");
    asm("swi 0"); //which call swi handler on 0x08. I thought.

while(1)
{
    gp_regs->GPSET[0]=0x40000;
    }
    return 0;
}

В чем проблема?

1 Ответ

0 голосов
/ 08 сентября 2018

Так что из тегов и тому подобного я предполагаю, что это Raspberry Pi3, в режиме aarch32, вероятно, в режиме HYP. Примечание. Я ценю, что вы читаете / заимствуете часть моего кода прямо или косвенно.

С вашим кодом давайте начнем здесь:

ldr r0,=0x00080008
mov r1,#0x0000

технически это не ошибка, но в некотором роде упущено, что делает эта копия.

b   print_mem1
b   print_mem1
b   print_mem2
b   print_mem3
b   print_mem4
b   print_mem1
b   print_mem2
b   print_mem3
b   print_mem4

в сочетании с этим, тогда да, это проблема. так как они зависят от позиции, и сама идея создания цепочки инструментов создаст для вас таблицу, тогда копирование будет потеряно.

Disassembly of section .text:

00080000 <_ram_entry>:
   80000:   eb00000a    bl  80030 <kernel_init>
   80004:   eafffffd    b   80000 <_ram_entry>
   80008:   e59ff074    ldr pc, [pc, #116]  ; 80084 <print_c_mem4+0x4>
   8000c:   ea000013    b   80060 <print_mem1>
   80010:   ea000012    b   80060 <print_mem1>
   80014:   ea000012    b   80064 <print_mem2>
   80018:   ea000012    b   80068 <print_mem3>
   8001c:   ea000012    b   8006c <print_mem4>
   80020:   ea00000e    b   80060 <print_mem1>
   80024:   ea00000e    b   80064 <print_mem2>
   80028:   ea00000e    b   80068 <print_mem3>
   8002c:   ea00000e    b   8006c <print_mem4>

Когда я собираю, а затем разбираю, ldr pc, который является правильным способом сделать это, но приземление в неправильном месте, показывает 0x80084, который на 84-8 = 0x7C вперед, что на 1111100 регистров 0x1F, используемых для копирования, чтобы получить так далеко ...

ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}

32 регистра, скопировано 0x80 байт. технически это охватывает первый вектор, может быть, второй, но, конечно, не вектор swi.

когда вы смотрите документацию по arm (снова) (armv7-ar, так как это режим совместимости aarch32 или armv7-a) 0x00000008 - это точка входа для вызова supervisor / svc / swi.

Итак, вам нужна инструкция, которая переходит от 0x00000008 к нужному адресу / метке.

поэтому, если вы вернетесь к этому примеру или к любому другому примеру, который вы позаимствовали / извлекли из него.

.globl _start
_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    ldr pc,irq_handler
    ldr pc,fiq_handler
reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word hang

reset:
    mov r0,#0x80000
    mov r1,#0x0000
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}




Disassembly of section .text:

00080000 <_stack>:
   80000:   e59ff018    ldr pc, [pc, #24]   ; 80020 <reset_handler>
   80004:   e59ff018    ldr pc, [pc, #24]   ; 80024 <undefined_handler>
   80008:   e59ff018    ldr pc, [pc, #24]   ; 80028 <swi_handler>
   8000c:   e59ff018    ldr pc, [pc, #24]   ; 8002c <prefetch_handler>
   80010:   e59ff018    ldr pc, [pc, #24]   ; 80030 <data_handler>
   80014:   e59ff018    ldr pc, [pc, #24]   ; 80034 <unused_handler>
   80018:   e59ff018    ldr pc, [pc, #24]   ; 80038 <irq_handler>
   8001c:   e59ff018    ldr pc, [pc, #24]   ; 8003c <fiq_handler>

00080020 <reset_handler>:
   80020:   00080040    andeq   r0, r8, r0, asr #32

00080024 <undefined_handler>:
   80024:   00080058    andeq   r0, r8, r8, asr r0

00080028 <swi_handler>:
   80028:   00080058    andeq   r0, r8, r8, asr r0

0008002c <prefetch_handler>:
   8002c:   00080058    andeq   r0, r8, r8, asr r0

00080030 <data_handler>:
   80030:   00080058    andeq   r0, r8, r8, asr r0

00080034 <unused_handler>:
   80034:   00080058    andeq   r0, r8, r8, asr r0

00080038 <irq_handler>:
   80038:   0008005c    andeq   r0, r8, ip, asr r0

0008003c <fiq_handler>:
   8003c:   00080058    andeq   r0, r8, r8, asr r0

00080040 <reset>:
   80040:   e3a00702    mov r0, #524288 ; 0x80000
   80044:   e3a01000    mov r1, #0
   80048:   e8b003fc    ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
   8004c:   e8a103fc    stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
   80050:   e8b003fc    ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
   80054:   e8a103fc    stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}

00080058 <hang>:
   80058:   eafffffe    b   80058 <hang>

0008005c <irq>:
   8005c:   eafffffe    b   8005c <irq>

Он вынуждает 8 слов точек входа запускаться из таблицы обработчиков исключений и помещает эти адреса для относительного доступа к ПК сразу после них в следующие 8 слов, поэтому вам нужно скопировать 16 слов, чтобы ассемблер выполнил работу. для вас и не надо вычислять эти вещи. 32 слова, 4 инструкции по 8 регистров, то есть 32 слова. или если вы предпочитаете 8 комплектов инструкций по 4 слова, каждое из которых тоже работает.

это то, что вам нужно с этим целым подходом

   80008:   e59ff018    ldr pc, [pc, #24]   ; 80028 <swi_handler>

00080028 <swi_handler>:
   80028:   00080058

заставить инструмент делать работу за вас

что если я сделаю это:

.globl _start
_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    b irq
    ldr pc,fiq_handler
reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word hang

reset:
    mov r0,#0x80000
    mov r1,#0x0000
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}

hang:
    b hang

irq:
    b irq

Я получаю это

   80018:   ea00000f    b   8005c <irq>

вместо этого

   80018:   e59ff018    ldr pc, [pc, #24]   ; 80038 <irq_handler>

последний говорит, что при чтении с ПК + 24 ПК в этом случае на 8 опережает этот адрес, поэтому адрес инструкции + 32, то есть адрес инструкции + 0x20.

и это

   80018:   ea00000f    b   8005c <irq>

говорит, что ответвляется по адресу 0x44 перед адресом инструкции

Теперь давайте разберем с другого базового адреса, например, отличный объект (вместо связанного двоичного файла elf)

00000000 <_start>:
   0:   e59ff018    ldr pc, [pc, #24]   ; 20 <reset_handler>
   4:   e59ff018    ldr pc, [pc, #24]   ; 24 <undefined_handler>
   8:   e59ff018    ldr pc, [pc, #24]   ; 28 <swi_handler>
   c:   e59ff018    ldr pc, [pc, #24]   ; 2c <prefetch_handler>
  10:   e59ff018    ldr pc, [pc, #24]   ; 30 <data_handler>
  14:   e59ff018    ldr pc, [pc, #24]   ; 34 <unused_handler>
  18:   ea00000f    b   5c <irq>
  1c:   e59ff018    ldr pc, [pc, #24]   ; 3c <fiq_handler>

Обратите внимание на машинный код для всех остальных, загрузите слово 0x20 байтов перед этой инструкцией в компьютер.

Где ветвь говорит ветвь 0x44 байта перед счетчиком программы.

Мы использовали цепочку инструментов, чтобы сделать эту таблицу

00080020 <reset_handler>:
   80020:   00080040    andeq   r0, r8, r0, asr #32

00080024 <undefined_handler>:
   80024:   00080058    andeq   r0, r8, r8, asr r0

00080028 <swi_handler>:
   80028:   00080058    andeq   r0, r8, r8, asr r0

0008002c <prefetch_handler>:
   8002c:   00080058    andeq   r0, r8, r8, asr r0

00080030 <data_handler>:
   80030:   00080058    andeq   r0, r8, r8, asr r0

00080034 <unused_handler>:
   80034:   00080058    andeq   r0, r8, r8, asr r0

00080038 <irq_handler>:
   80038:   0008005c    andeq   r0, r8, ip, asr r0

0008003c <fiq_handler>:
   8003c:   00080058    andeq   r0, r8, r8, asr r0

Если мы скопируем 0x40 байтов из 0x80000 в 0x00000, то когда он совпадет с машинным кодом 0x18, в котором написано чтение из 0x38, и поместит его в счетчик программ, он получит 0008005c, что является правильным местом

но если вместо этого он найдет

18: ea00000f    b   5c <irq>

это означает переход к 0x5c, где у нас нет обработчика.

кроме установки указателя стека и того, как ваш код попал на swi, но в любом случае, если вы создали это

80008:  e59ff074    ldr pc, [pc, #116]  ; 80084 <print_c_mem4+0x4>
   8000c:   ea000013    b   80060 <print_mem1>
   80010:   ea000012    b   80060 <print_mem1>
   80014:   ea000012    b   80064 <print_mem2>
   80018:   ea000012    b   80068 <print_mem3>
   8001c:   ea000012    b   8006c <print_mem4>
   80020:   ea00000e    b   80060 <print_mem1>
   80024:   ea00000e    b   80064 <print_mem2>
   80028:   ea00000e    b   80068 <print_mem3>
   8002c:   ea00000e    b   8006c <print_mem4>

или что-то в этом роде, поскольку ваши print_mems - это не просто заполнители, чтобы получить этот пример для этого ответа. но все равно пк относительные ветви.

и вы на некоторое время скопировали с 0x80008 в 0x00000, после чего инструкция, которая в итоге оказывается по адресу 0x00000008, который является обработчиком svc / swi, является

   80010:   ea000012    b   80060 <print_mem1>

ветвь к print_mem1, но она не собирается идти где-нибудь рядом с print_mem1, потому что она собирается разветвлять некоторое количество байтов после 0x00000, что будет на расстоянии 0x80008 байт от адреса, на котором вы действительно хотели, чтобы он приземлился.

Теперь, сказав ВСЕ об этом, если вы будете искать HVBAR в документации на руку, вы обнаружите, что вам не нужно делать какое-либо из этого копирования, вы можете настроить таблицу исключений в памяти и изменить базовый адрес того места, куда идет процессор, когда возникает исключение (кроме сброса). но обратите внимание, что младшие 5 бит должны быть равны нулю, поэтому 0x80008 не будет работать. Так что используйте .balign в своем коде, создайте таблицу там, используйте метки, чтобы получить адрес этого, и вставьте его в HVBAR, после чего вы можете использовать branch вместо ldr pc. Для armv6 и старше копирование или сборка таблицы (таблиц) должны быть выполнены, потому что, кроме процессорной планки для высокого адреса, векторы должны быть в 0x00000000. Для armv7 и нескольких кортекс-мс вы можете вместо этого перемещать / указывать на таблицу по другому адресу (до сброса).

Хорошо понимать тот трюк копирования, который я продемонстрировал, но вы должны использовать его правильно, чтобы он работал. Это не редкое решение. Обратите внимание на другой способ сделать это, и вы можете сделать это здесь:

.globl _start
_start:
    b 0x80000
    b 0x80004
    b 0x80008
    b 0x8000C

при соединении в 0x0000

00000000 <_start>:
   0:   ea01fffe    b   80000 <_stack>
   4:   ea01fffe    b   80004 <*ABS*0x80004>
   8:   ea01fffe    b   80008 <*ABS*0x80008>
   c:   ea01fffe    b   8000c <*ABS*0x8000c>

так что этот машинный код ea01fffe означает переход к 0x80000 относительно адреса этой инструкции, поэтому вместо копии вы можете просто написать первые 8 слов, начиная с 0x00000000, и процессор перейдет к вашей таблице 0x80000. Если вы хотите построить его в 0x80008, пусть инструменты сделают всю работу за вас:

.globl _start
_start:
    b 0x80008
    b 0x8000c
    b 0x80010
    b 0x80014

как и ожидалось, непосредственное количество слов, 0x8 - два слова, добавьте 2 к 1, если вы получите 0x20000

00000000 <_start>:
   0:   ea020000    b   80008 <*ABS*0x80008>
   4:   ea020000    b   8000c <*ABS*0x8000c>
   8:   ea020000    b   80010 <*ABS*0x80010>
   c:   ea020000    b   80014 <*ABS*0x80014>

также мы знаем, что ПК на два впереди, поэтому при выполнении по адресу 0 ПК при использовании этого способа равен 8, мы хотим перейти к 0x80008, что на 0x80000 впереди ПК, непосредственное значение в инструкции выражается в единицах слова так 0x20000 слов впереди.

Так что вместо копии

ldr r0,=0xEA020000
ldr r1,=0x00000000
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4

или какое-либо другое решение, которое заполнит эти 8 мест веткой в ​​нужном месте.

РЕДАКТИРОВАТЬ

Еще один подход, позволяющий инструментам сделать это для нас:

Разборка раздела .text:

00080000 <_stack>:
   80000:   e59ff018    ldr pc, [pc, #24]   ; 80020 <reset_handler>
   80004:   e59ff018    ldr pc, [pc, #24]   ; 80024 <undefined_handler>
   80008:   e59ff018    ldr pc, [pc, #24]   ; 80028 <swi_handler>
   8000c:   e59ff018    ldr pc, [pc, #24]   ; 8002c <prefetch_handler>
   80010:   e59ff018    ldr pc, [pc, #24]   ; 80030 <data_handler>
   80014:   e59ff018    ldr pc, [pc, #24]   ; 80034 <unused_handler>
   80018:   e59ff018    ldr pc, [pc, #24]   ; 80038 <irq_handler>
   8001c:   e59ff018    ldr pc, [pc, #24]   ; 8003c <fiq_handler>

Это то, что мы можем заполнить первые 8 слов памяти e59ff018, а затем в какой-то момент, прежде чем они нам понадобятся, заполнить адреса позже, перед созданием прерываний, заполнить 0x00000038 адресом для обработчика, можно использовать C или ASM или что угодно. Может каждый раз менять обработчик, помещать 0xe59ff018 в память по адресу 0x00000008 и адрес вашего обработчика swi по адресу 0x00000028 перед выполнением инструкции svc / swi, измените обработчик в 0x00000028 и повторите попытку.

...