在64位CPU上使用32位指令“swi”

问题描述 投票:1回答:1

我正在使用raspberry pi 3制作我的内核(没有蓝牙)。我的内核使用arm汇编语言(32位),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;
}

有什么问题?

c operating-system arm raspberry-pi3 interrupt
1个回答
1
投票

所以从标签等我认为这是一个覆盆子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-兼容模式)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个单词中将这些地址放在pc相对访问权限之后,这样你就需要复制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>

后者是说从pc + 24读取,在这种情况下,pc是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字节加载到pc中。

分支表示程序计数器前面的分支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不只是占位符来得到这个例子来构建这个答案。但仍然是pc相对分支机构。

你从0x80008复制了一段时间到0x00000然后最终在地址0x00000008这是svc / swi处理程序的指令是

   80010:   ea000012    b   80060 <print_mem1>

print_mem1的分支,但它不会去print_mem1附近的任何地方,因为它将在0x00000之后分支一些字节,这将远离你真正希望它着陆的地址0x80008字节。

现在说了所有这些,如果你在arm文档中搜索HVBAR,你会发现你不必进行任何复制,你可以在内存中设置一个异常表,并更改处理器在异常时所到之处的基址(除了重置之外)。但请注意,低5位必须为零,因此0x80008不起作用。因此,在代码中使用.balign,在那里构建表,使用标签获取地址并将其粘贴在HVBAR中,然后可以使用branch而不是ldr pc。对于armv6及更早版本,需要完成复制或构建表,因为除了用于高地址的处理器带之外,向量必须是0x00000000。对于armv7和一些cortex-ms,您可以改为在其他地址的桌子上移动/指向(直到重置)。

很好理解我演示的复制技巧,但你必须正确使用它才能工作。这不是一个不寻常的解决方案。请注意另一种方法,你可以这样做:

.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,因此您可以只写下从0x00000000开始的前8个字而不是副本,处理器将分支到您的0x80000表。如果你想在0x80008构建它,那么让工具为你工作:

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

正如预期的那样,立即是字数,0x8是两个字加2到1fffe你得到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>

我们也知道pc是提前两个所以当在地址0执行时,这种方式使用的pc是8我们要在pc8之前转到0x80008,即0x80000,指令中的immediate是以字为单位所以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>

我们是否可以用e59ff018填充内存的前8个字然后在我们需要它们之前的某个时刻可以在以后填写地址,在创建中断之前用处理器的地址填充0x00000038,可以使用C或ASM等等。每次都可以更改处理程序,在执行svc / swi指令之前将0xe59ff018放在内存中0x00000008并将地址放到swi处理程序0x00000028,将处理程序更改为0x00000028并再试一次。

© www.soinside.com 2019 - 2024. All rights reserved.