ARMv8异常向量和处理

时间:2017-07-08 22:25:16

标签: 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

还有什么我想念的吗? 任何帮助或指向参考将不胜感激。

1 个答案:

答案 0 :(得分:0)

所以我已经有了一个例子,你可以学习,接受或离开它......

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
}

vectors.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

PERIPH.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,没有任何其他内核* .img具有该名称(根据需要重命名或删除)。

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

pi3在EL3中启动,因为人们会认为它们不会弄乱它(如果你甚至可以从核心的边缘,可以肯定地转换aarch32与aarch64)。

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

我还没想到的是为什么WFI在中断发生时不释放。