assembly - ARMv8 异常向量和处理

标签 assembly arm arm64

我正在开发 ARM Cortex A53 处理器,但不知道如何设置中断来工作。

我已阅读有关此主题的文档,但仍然发现它令人困惑,并且无法在裸机环境中中断工作。

这是我当前拥有的向量表:

_vectors:
    /* Current EL with SP0 */
    b sync_addr /* Synchronous */
    .balign 128
    b irq_addr  /* IRQ/vIRQ */
    .balign 128
    b fiq_addr  /* FIQ/vFIQ */
    .balign 128
    b serr_addr /* SError/vSError */
    /* Current EL with SPn */
    b sync_addr /* Synchronous */
    .balign 128
    b irq_addr  /* IRQ/vIRQ */
    .balign 128
    b fiq_addr  /* FIQ/vFIQ */
    .balign 128
    b serr_addr /* SError/vSError */
    /* Lower EL with Aarch64 */
    b sync_addr /* Synchronous */
    .balign 128
    b irq_addr  /* IRQ/vIRQ */
    .balign 128
    b fiq_addr  /* FIQ/vFIQ */
    .balign 128
    b serr_addr /* SError/vSError */
    /* Lower EL with Aarch32 */
    b sync_addr /* Synchronous */
    .balign 128
    b irq_addr  /* IRQ/vIRQ */
    .balign 128
    b fiq_addr  /* FIQ/vFIQ */
    .balign 128
    b serr_addr /* SError/vSError */

sync_addr: .word reset_handler
irq_addr:  .word irq_handler
fiq_addr:  .word reset_handler
serr_addr: .word reset_handler

我是通过《ARMv8 程序员指南》第 10.4 节得到的。

据我所知,我需要将 VBAR_ELn 寄存器设置为指向该表,我这样做:

ldr x0, =_vectors
msr vbar_el1, x0

我还有什么遗漏的吗? 任何帮助或指向引用将不胜感激。

最佳答案

所以我已经有一个例子,你可以学习,接受或放弃......

config.txt(位于 SD 卡上):

arm_control=0x200
kernel_old=1
disable_commandline_tags=1

memmap(链接描述文件):

MEMORY
{
    ram : ORIGIN = 0x0000, LENGTH = 0x1000000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}

向量.s

.globl _start
_start:
    b skip
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b hang
.balign 128
    b irq_handler

skip:
    // isolate core 0
    mrs x0,mpidr_el1
    mov x1,#0xC1000000
    bic x1,x0,x1
    cbz x1,zero
not_zero:
    wfi
    //msr daifset,#2
    b not_zero
zero:

    mov sp,#0x08000000
    bl notmain
hang: b hang

.globl PUT32
PUT32:
  str w1,[x0]
  ret

.globl GET32
GET32:
    ldr w0,[x0]
    ret

.globl enable_irq
enable_irq:
    //should already be set here
    ldr x1,=0x00000000
    msr vbar_el3,x1
    //route to EL3
    mrs x0,scr_el3
    orr x0,x0,#8
    orr x0,x0,#4
    orr x0,x0,#2
    msr scr_el3,x0
    //clear/enable irq bit in PSTATE
    msr daifclr,#2
    ret

irq_handler:
    //19 up are callee saved
    //so we have to preserve all below?
    stp x0,x1,[sp,#-16]!
    stp x2,x3,[sp,#-16]!
    stp x4,x5,[sp,#-16]!
    stp x6,x7,[sp,#-16]!
    stp x8,x9,[sp,#-16]!
    stp x10,x11,[sp,#-16]!
    stp x12,x13,[sp,#-16]!
    stp x14,x15,[sp,#-16]!
    stp x16,x17,[sp,#-16]!
    stp x18,x19,[sp,#-16]!

    //mrs x0,esr_el3
    bl c_irq_handler

    ldp x18,x19,[sp],#16
    ldp x16,x17,[sp],#16
    ldp x14,x15,[sp],#16
    ldp x12,x13,[sp],#16
    ldp x10,x11,[sp],#16
    ldp x8,x9,[sp],#16
    ldp x6,x7,[sp],#16
    ldp x4,x5,[sp],#16
    ldp x2,x3,[sp],#16
    ldp x0,x1,[sp],#16
    eret

.globl DOWFI
DOWFI:
    wfi
    //msr daifset,#2
    ret

外围.c

#define PBASE 0x3F000000

extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern void dummy ( unsigned int );

#define ARM_TIMER_CTL   (PBASE+0x0000B408)
#define ARM_TIMER_CNT   (PBASE+0x0000B420)

#define GPFSEL1         (PBASE+0x00200004)
#define GPSET0          (PBASE+0x0020001C)
#define GPCLR0          (PBASE+0x00200028)
#define GPPUD           (PBASE+0x00200094)
#define GPPUDCLK0       (PBASE+0x00200098)

#define AUX_ENABLES     (PBASE+0x00215004)
#define AUX_MU_IO_REG   (PBASE+0x00215040)
#define AUX_MU_IER_REG  (PBASE+0x00215044)
#define AUX_MU_IIR_REG  (PBASE+0x00215048)
#define AUX_MU_LCR_REG  (PBASE+0x0021504C)
#define AUX_MU_MCR_REG  (PBASE+0x00215050)
#define AUX_MU_LSR_REG  (PBASE+0x00215054)
#define AUX_MU_MSR_REG  (PBASE+0x00215058)
#define AUX_MU_SCRATCH  (PBASE+0x0021505C)
#define AUX_MU_CNTL_REG (PBASE+0x00215060)
#define AUX_MU_STAT_REG (PBASE+0x00215064)
#define AUX_MU_BAUD_REG (PBASE+0x00215068)

//GPIO14  TXD0 and TXD1
//GPIO15  RXD0 and RXD1

unsigned int uart_lcr ( void )
{
    return(GET32(AUX_MU_LSR_REG));
}

unsigned int uart_recv ( void )
{
    while(1)
    {
        if(GET32(AUX_MU_LSR_REG)&0x01) break;
    }
    return(GET32(AUX_MU_IO_REG)&0xFF);
}

unsigned int uart_check ( void )
{
    if(GET32(AUX_MU_LSR_REG)&0x01) return(1);
    return(0);
}

void uart_send ( unsigned int c )
{
    while(1)
    {
        if(GET32(AUX_MU_LSR_REG)&0x20) break;
    }
    PUT32(AUX_MU_IO_REG,c);
}

void uart_flush ( void )
{
    while(1)
    {
        if(GET32(AUX_MU_LSR_REG)&0x40) break;
    }
}

void hexstrings ( unsigned int d )
{
    //unsigned int ra;
    unsigned int rb;
    unsigned int rc;

    rb=32;
    while(1)
    {
        rb-=4;
        rc=(d>>rb)&0xF;
        if(rc>9) rc+=0x37; else rc+=0x30;
        uart_send(rc);
        if(rb==0) break;
    }
    uart_send(0x20);
}

void hexstring ( unsigned int d )
{
    hexstrings(d);
    uart_send(0x0D);
    uart_send(0x0A);
}

void uart_init ( void )
{
    unsigned int ra;

    PUT32(AUX_ENABLES,1);
    PUT32(AUX_MU_IER_REG,0);
    PUT32(AUX_MU_CNTL_REG,0);
    PUT32(AUX_MU_LCR_REG,3);
    PUT32(AUX_MU_MCR_REG,0);
    PUT32(AUX_MU_IER_REG,0);
    PUT32(AUX_MU_IIR_REG,0xC6);
    PUT32(AUX_MU_BAUD_REG,270);
    ra=GET32(GPFSEL1);
    ra&=~(7<<12); //gpio14
    ra|=2<<12;    //alt5
    ra&=~(7<<15); //gpio15
    ra|=2<<15;    //alt5
    PUT32(GPFSEL1,ra);
    //PUT32(GPPUD,0);
    //for(ra=0;ra<150;ra++) dummy(ra);
    //PUT32(GPPUDCLK0,(1<<14)|(1<<15));
    //for(ra=0;ra<150;ra++) dummy(ra);
    //PUT32(GPPUDCLK0,0);
    PUT32(AUX_MU_CNTL_REG,3);
}

notmain.c

extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern void dummy ( unsigned int );
extern void enable_irq ( void );
extern void DOWFI ( void );

extern void uart_init ( void );
extern void uart_send ( unsigned int );
extern void hexstring ( unsigned int );

#define GPFSEL2 0x3F200008
#define GPSET0  0x3F20001C
#define GPCLR0  0x3F200028


#define ARM_TIMER_LOD 0x3F00B400
#define ARM_TIMER_VAL 0x3F00B404
#define ARM_TIMER_CTL 0x3F00B408
#define ARM_TIMER_CLI 0x3F00B40C
#define ARM_TIMER_RIS 0x3F00B410
#define ARM_TIMER_MIS 0x3F00B414
#define ARM_TIMER_RLD 0x3F00B418
#define ARM_TIMER_DIV 0x3F00B41C
#define ARM_TIMER_CNT 0x3F00B420

#define SYSTIMERCLO 0x3F003004
#define GPFSEL1 0x3F200004
#define GPSET0  0x3F20001C
#define GPCLR0  0x3F200028
#define GPFSEL3 0x3F20000C
#define GPFSEL4 0x3F200010
#define GPSET1  0x3F200020
#define GPCLR1  0x3F20002C

#define IRQ_BASIC 0x3F00B200
#define IRQ_PEND1 0x3F00B204
#define IRQ_PEND2 0x3F00B208
#define IRQ_FIQ_CONTROL 0x3F00B210
#define IRQ_ENABLE_BASIC 0x3F00B218
#define IRQ_DISABLE_BASIC 0x3F00B224

volatile unsigned int icount;


void c_irq_handler ( void )
{
    icount++;
    if(icount&1)
    {
        PUT32(GPSET0,1<<21);
        uart_send(0x55);
    }
    else
    {
        PUT32(GPCLR0,1<<21);
        uart_send(0x56);
    }
    PUT32(ARM_TIMER_CLI,0);
}

int notmain ( void )
{
    unsigned int ra;

    PUT32(IRQ_DISABLE_BASIC,1);

    ra=GET32(GPFSEL2);
    ra&=~(7<<3);
    ra|=1<<3;
    PUT32(GPFSEL2,ra);

    uart_init();
if(1)
{
    PUT32(ARM_TIMER_CTL,0x003E0000);
    PUT32(ARM_TIMER_LOD,1000000-1);
    PUT32(ARM_TIMER_RLD,1000000-1);
    PUT32(ARM_TIMER_DIV,0x000000F9);
    PUT32(ARM_TIMER_CLI,0);
    PUT32(ARM_TIMER_CTL,0x003E00A2);

    for(ra=0;ra<2;ra++)
    {
        PUT32(GPSET0,1<<21);
        uart_send(0x55);
        while(1) if(GET32(ARM_TIMER_MIS)) break;
        PUT32(ARM_TIMER_CLI,0);

        PUT32(GPCLR0,1<<21);
        uart_send(0x56);
        while(1) if(GET32(ARM_TIMER_MIS)) break;
        PUT32(ARM_TIMER_CLI,0);
    }
    uart_send(0x0D);
    uart_send(0x0A);
}
if(1)
{
    PUT32(ARM_TIMER_CTL,0x003E0000);
    PUT32(ARM_TIMER_LOD,2000000-1);
    PUT32(ARM_TIMER_RLD,2000000-1);
    PUT32(ARM_TIMER_CLI,0);
    PUT32(IRQ_ENABLE_BASIC,1);
    PUT32(ARM_TIMER_CTL,0x003E00A2);
    for(ra=0;ra<3;ra++)
    {
        PUT32(GPSET0,1<<21);
        uart_send(0x55);
        while(1) if(GET32(IRQ_BASIC)&1) break;
        PUT32(ARM_TIMER_CLI,0);

        PUT32(GPCLR0,1<<21);
        uart_send(0x56);
        while(1) if(GET32(IRQ_BASIC)&1) break;
        PUT32(ARM_TIMER_CLI,0);
    }
    PUT32(IRQ_ENABLE_BASIC,0);
    uart_send(0x0D);
    uart_send(0x0A);
}
    PUT32(ARM_TIMER_CTL,0x003E0000);
    PUT32(ARM_TIMER_LOD,500000-1);
    PUT32(ARM_TIMER_RLD,500000-1);
    PUT32(ARM_TIMER_CLI,0);
    PUT32(IRQ_ENABLE_BASIC,1);
    icount=0;
    enable_irq();
    PUT32(ARM_TIMER_CTL,0x003E00A2);
    PUT32(ARM_TIMER_CLI,0);

    while(1)
    {
        DOWFI();
        uart_send(0x33);
    }
    return(0);
}

构建

aarch64-none-elf-as --warn --fatal-warnings vectors.s -o vectors.o
aarch64-none-elf-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding  -c periph.c -o periph.o
aarch64-none-elf-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding  -c notmain.c -o notmain.o
aarch64-none-elf-ld vectors.o periph.o notmain.o -T memmap -o notmain.elf
aarch64-none-elf-objdump -D notmain.elf > notmain.list
aarch64-none-elf-objcopy notmain.elf -O binary kernel8.img

然后检查反汇编以确认一切都在正确的位置。我向 vbar 写入零,尽管我不需要这样做,因为它应该显示为零。那么 EL3 的地址/偏移量需要是 0x280 或者不是 el0/el1 是什么?类似的事情。

0000000000000000 <_start>:
   0:   140000a1    b   284 <skip>
   4:   140000a8    b   2a4 <hang>
   8:   140000a7    b   2a4 <hang>
   c:   140000a6    b   2a4 <hang>
  10:   140000a5    b   2a4 <hang>
  14:   140000a4    b   2a4 <hang>
  18:   140000a3    b   2a4 <hang>
  1c:   140000a2    b   2a4 <hang>
  20:   d503201f    nop
  24:   d503201f    nop
  28:   d503201f    nop
  2c:   d503201f    nop
...
 27c:   d503201f    nop
 280:   14000017    b   2dc <irq_handler>
...

我的猜测是向量之间存在很大的差距,因此您可以将处理程序放在那里而不是在其他地方分支,但无论如何我都在其他地方分支。

并复制 kernel8.img 和 config.txt(以及 bootcode.bin 和 start.elf),并且没有任何其他具有该名称的 kernel*.img(根据需要重命名或删除)。

第一个 if(1) 轮询外设中的中断。接下来是特定于芯片的,因为树莓派 3 有自己的中断逻辑,他们绑定(bind)了 GICCDISABLE 来禁用 GIC,所以不要玩这个。最后一个中断是允许的。

EL3 中的 pi3 启动正如人们所期望的那样,它们不会弄乱它(如果你甚至可以从核心的边缘,当然可以切换 aarch32 与 aarch64)。

因此,从您离开的地方开始,您需要确保矢量位于正确的位置/与您设置 vbar 的位置的偏移量。我会假设但必须检查 vbar 是否有对齐要求,也许没有(某些数量的较低地址位必须为零)。然后你就可以完成所有外围工作了。 pi 很好(其他的则不然),因为一旦您弄清楚如何使外设发送中断,您就可以轮询中断状态线以查看外设的哪些线发生变化。使用 Broadcom 文档验证中断线路/编号,看看是否有意义,其中一些没有记录,或者您可能必须查看社区驱动的勘误表等。然后就很容易允许中断通过,而不是那么复杂作为 gic 或其他中断 Controller (如果您不想,不需要在这里做优先级的事情),然后允许它进入核心。

我还没弄清楚为什么WFI在中断发生时没有释放。

关于assembly - ARMv8 异常向量和处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44991264/

相关文章:

assembly - 为什么我们在 CMPXCHG 之前需要锁定前缀

浮点表达式的汇编时求值

c++ - 卡在跟踪 SIGFPE/算术异常

c - 将 watchdog_set_period 设置为最大值会导致重启

ios - 仅将 arm64 保留为有效架构的后果?

assembly - 无法将寄存器移动到不同大小的寄存器

gcc - 使用 Arm GNU 汇编器弹出推送

android - 交叉编译到arm时找不到lpthread

assembly - ARM ;或开关;汇编;

assembly - ARM 程序集加密扩展