yangyong9608老兄请进,回你eboot中断向量问题

frankwoods   2009-6-19 15:42 楼主
作者:wogoyixikexie@gliet(转载请注明)
        前几天这个帖子http://topic.eeworld.net/u/20090605/12/abd72d4e-260c-4e72-966f-298ed343ed3c.html搞的一头雾水,现在看懂了一些,
特写篇博客
  我一直使用的是ADS 的bootloader (STEPLDR+eboot整合在一起了),对PB下的bootloader不是很了解,现在有点时间,正好来看看。
  像所有bootloader一样,STEPLDR开始就是复位,PB下的bootloader有个特殊情况,就是没有完整的中断向量表,所以是不支持中断的,
  要稍作修改才行。我的ADS bootloader具有完整的中断向量以及相关栈的初始化,支持中断。
STARTUPTEXT
    LEAF_ENTRY StartUp
   
    b        ResetHandler  
    b        .
    b        .
    b        .               
    b        .               
    b        .               
    b        .                       
    b        .

有此可以看出,STEPLDR只是对复位进行了处理。如果发生别的中断就会B . 死循环了。
  这里要提醒的是在eboot中发生的中断时跳到STEPLDR存放中断向量表的地方的,以前我居然认为是跳到eboot中断向量表的地方,真是雷死人了。在ARM结构中,一旦发生中断,PC就会指向0x00000000(bootloader中使用)或者0xffff0000(wince 下使用),所以eboot发生中断,很明显应该跳到STEPLDR的0x00000000,eboot一般已经开了MMU,那么发生中断应该是跳到0x00000000对应的虚拟地址就可以了。
  STEPLDR还没有开MMU的,其实STEPLDR就是被自动加载到2440 内部4K SRAM,并完成拷贝eboot到内存并跳转到eboot运行的功能。STEPLDR代码较简单,无非是实现一些复位,休眠唤醒,软复位等功能,在这里就不再赘述了。
  现在我就来看PB 的bootloader下的难点——eboot中是如何初始化内存映射表的?
  在eboot的startup.s中有
;   Define RAM space for the Page Tables:
;
PHYBASE         EQU     0x30000000      ; physical start
PTs             EQU     0x30010000      ; 1st level page table address (PHYBASE + 0x10000)
                                        ; save room for interrupt vectors.前面用来保存中断向量

;-------------------------------------------------------------------------------
下面是内存映射表初始化的地方
下面的注释,很大部分是参考我一个师兄的杰作,在这里贴出他文章地址
http://blog.chinaunix.net/u1/38994/showart_1881778.html
和大家分享一下。
还有我去年的老帖子地址http://topic.eeworld.net/u/20081231/10/BBDE79C2-2884-48E3-9718-90D7FCC1AFA8.html


; Compute physical address of the OEMAddressTable.
20      add     r11, pc, #g_oalAddressTable - (. + 8)
        ldr     r10, =PTs                ; (r10) = 1st level page table
; r10=0x30010000


        ; Setup 1st level page table (using section descriptor)     
        ; Fill in first level page table entries to create "un-mapped" regions
        ; from the contents of the MemoryMap array.
        ;
        ;   (r10) = 1st level page table
        ;   (r11) = ptr to MemoryMap array
;0x2000=0x80000000 >> 18 具体原因参考MMU设置 说明这是从cached 地址0x80000000开始的
        add     r10, r10, #0x2000       ; (r10) = ptr to 1st PTE for "unmapped space"
        mov     r0, #0x0E               ; (r0) = PTE for 0: 1MB cachable bufferable
        orr     r0, r0, #0x400          ; set kernel r/w permission
25      mov     r1, r11                 ; (r1) = ptr to MemoryMap array

        
30      ldr     r2, [r1], #4            ; (r2) = virtual address to map Bank at
        ldr     r3, [r1], #4            ; (r3) = physical address to map from
        ldr     r4, [r1], #4            ; (r4) = num MB to map

        cmp     r4, #0                  ; End of table?
        beq     %f40

        ldr     r5, =0x1FF00000
        and     r2, r2, r5              ; VA needs 512MB, 1MB aligned.               

        ldr     r5, =0xFFF00000
        and     r3, r3, r5              ; PA needs 4GB, 1MB aligned.

        add     r2, r10, r2, LSR #18    ;这是MMU设置决定的,再左移回去
        add     r0, r0, r3              ; (r0) = PTE for next physical page

35      str     r0, [r2], #4            ;物理地址存到相应的虚拟地址
        add     r0, r0, #0x00100000     ; (r0) = PTE for next physical page 1M
        sub     r4, r4, #1              ; Decrement number of MB left
        cmp     r4, #0
        bne     %b35                    ; Map next MB

        bic     r0, r0, #0xF0000000     ; Clear Section Base Address Field
        bic     r0, r0, #0x0FF00000     ; Clear Section Base Address Field
        b       %b30                    ; Get next element

//创建对应的0xa0000000开始的uncached映射MMU表   
//比较C高速缓存是否仍然置位,如果仍然置位了,那么说明还没有执行uncached创建,
//如果C位已经清0,那么说明,uncached循环也执行完毕了,所以跳回到25标号继续创建   
40      tst     r0, #8
        bic     r0, r0, #0x0C           ; clear cachable & bufferable bits in PTE //清除B写缓冲和C高速缓存
//r10现在对应0x80000000虚拟地址的PTR起始地址,hex(0x20000000>>18)为0x800,
//所以r10 = r10 + 0x800;之后r10指向了0xa0000000虚拟地址对应的PTR起始地址      
        add     r10, r10, #0x0800       ; (r10) = ptr to 1st PTE for "unmapped uncached space"
        bne     %b25                    ; go setup PTEs for uncached space
        // 下面这句没有看出有什么作用哦。
        sub     r10, r10, #0x3000       ; (r10) = restore address of 1st level page table

        ; Setup mmu to map (VA == 0) to (PA == 0x30000000).
        ldr     r0, =PTs                ; PTE entry for VA = 0  重新加载PTs ,这里没有加上偏移,相当于偏移了0
        //所以,这个虚拟地址就是0了,从整体来看,就是物理地址 0x30000000映射到了0x00000000了,汇编真的太难看懂了
        ldr     r1, =0x3000040E         ; uncache/unbuffer/rw, PA base == 0x30000000
        str     r1, [r0]

        ; uncached area.加上了0x0800=0x2000000>>18相当于是uncached地址了,不过中断向量的地址是cached的
        add     r0, r0, #0x0800         ; PTE entry for VA = 0x0200.0000 , uncached     
        ldr     r1, =0x30000402         ; uncache/unbuffer/rw, base == 0x30000000
        str     r1, [r0]
        
        ; Comment:
        ; The following loop is to direct map RAM VA == PA. i.e.
        ;   VA == 0x30XXXXXX => PA == 0x30XXXXXX for S3C2400
        ; Fill in 8 entries to have a direct mapping for DRAM
        ;
        ldr     r10, =PTs               ; restore address of 1st level page table
        ldr     r0,  =PHYBASE           // 内存起始物理地址0x30000000
//0x3000 / 4=0x30000000>>18 有时候不知道为什么三星会这么变态,明明是这样了,还不注释好点,或者直接写0x30000000>>18
//让人看了相当郁闷,不知所云。0x30000000映射到0x30000000,似乎没有这个必要。
        add     r10, r10, #(0x3000 / 4) ; (r10) = ptr to 1st PTE for 0x30000000

        add     r0, r0, #0x1E           ; 1MB cachable bufferable
        orr     r0, r0, #0x400          ; set kernel r/w permission
        mov     r1, #0
        mov     r3, #64
45      mov     r2, r1                  ; (r2) = virtual address to map Bank at
        cmp     r2, #0x20000000:SHR:BANK_SHIFT   //512 M 比较
        add     r2, r10, r2, LSL #BANK_SHIFT-18
        strlo   r0, [r2]
        add     r0, r0, #0x00100000     ; (r0) = PTE for next physical page
        subs    r3, r3, #1
        add     r1, r1, #1
        bgt     %b45

        ldr     r10, =PTs               ; (r10) = restore address of 1st level page table
        
; The page tables and exception vectors are setup.
        ; Initialize the MMU and turn it on.
        mov     r1, #1
        mcr     p15, 0, r1, c3, c0, 0   ; setup access to domain 0
        mcr     p15, 0, r10, c2, c0, 0  // C2 -------ldr     r10, =PTs
//The CP15:c2 register holds the translation table base address (TTB)—
//an address pointing to the location of the master L1 table in virtual memory(指出L1页表在虚拟内存中的地址).
        mcr     p15, 0, r0, c8, c7, 0   ; flush I+D TLBs
        mov     r1, #0x0071             ; Enable: MMU
        orr     r1, r1, #0x0004         ; Enable the cache

        ldr     r0, =VirtualStart

        cmp     r0, #0                  ; make sure no stall on "mov pc,r0" below
        mcr     p15, 0, r1, c1, c0, 0
        mov     pc, r0                  ;  & jump to new virtual address
        nop

        ; MMU & caches now enabled.
        ;   (r10) = physcial address of 1st level page table
        ;

VirtualStart

        mov     sp, #0x8C000000
        add     sp, sp, #0x30000        ; arbitrary initial super-page stack pointer
        b       main                    哈哈,跳到main函数了。
        
现在看来,关于上次eboot中断向量表的讨论,终于有结果了。
因为上面有虚拟地址0 映射到物理地址0x30000000,   虚拟地址0x30000000 映射到物理地址0x30000000
虚拟地址0x8c000000 映射到物理地址0x30000000  

在eboot中加入中断的堆栈初始化后,用下面几个地址就可以了。
mrs                r0, cpsr
                bic                r0, r0, #MODEMASK
                orr                r1, r0, #IRQMODE|NOINT
                msr                cpsr_cxsf, r1                ; IRQMode
                ldr                sp, = IRQStack
               
                bic                r0, r0, #MODEMASK|NOINT
                orr                r1, r0, #SVCMODE
                msr                cpsr_cxsf, r1                ; SVCMode.
                ldr                sp, = SVCStack
               
               
#define pISR (*(unsigned *)(0x30000000+0x18))
0x30000000换成过0x00000000,0x8C000000,都可以正常工作
pISR =(unsigned)(0xEA000000)+(((unsigned)IsrHandler - (0x8C000000 + 0x18 + 0x8) )>>2);   
上面这句相当于在C语言里面初始化了中断向量表
B IRQHandler     
计算原理:B 偏移地址
偏移地址=IsrHandler 所在地址-(boot.bib的起始地址+0x18(这个大家都知道了吧)+0x8(这个是ARM流水线,具体Google了))》2
为什么要友谊两位?这也是B指令的原因。Google一下就明白了。
1、 B 指令
            B 指令的格式为:
            B{条件} 目标地址
            B 指令是最简单的跳转指令。一旦遇到一个 B 指令,ARM
处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC 值的一个偏移量,
而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。
它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB 的地址空间)。

现在看来这个和ADS是一致的。
在ADS中,为了支持JLINK等调试
加入了如下映射。
MMU_SetMTT([0x00000000,0x0100000, 0x30000000,RW_CB);
其中0x30000000是ADS的RO base设置地址,是物理地址。
0x00000000是虚拟地址,在JLINK等调试的时候,中断向量表其实已经搬运到内存的0x30000000开始地方。
而2440 此时MMU已经打开,不能识别0x30000000物理地址,一旦中断,就会跳转到0x00000000这个虚拟地址去执行相应的中断handler
(*^__^*) 嘻嘻……,我是这么理解的,不知道对不对,这个问题已经困扰我好久了,以前似懂非懂,现在我觉得这应该是正确的。
在wince下,中断时候就是跳转到0xffff0000地方,我看过下面的代码,确实如此。所以ARM相关文档中0xffff0000以及0x00000000这两个
存放中断向量表的地方应该指的是虚拟地址。
        那么,在PB下的2440 eboot+STEPLDR中也应该是一样的。
        经过上面的层层映射,0x00000000、0x8C000000以及0x30000000这三个虚拟地址都同时指向物理地址0x30000000,所以这三个虚拟地址
可以互相代替,也是一样的效果,这恰恰证明了MMU的高明——多个虚拟地址可以对应一个物理地址!
        不过,在这里我还是有两个疑问。
        一、STEPLDR的代码是在2440 的内部4K SRAM 运行的,那中断向量表应该还是在0x0000 0000处才对,难道这时候2440 就把0x0000 0000
        看成是虚拟地址了?0x00000000、0x8C000000以及0x30000000这三个虚拟地址都同时指向物理地址0x30000000,但是0x30000000好像和
        中断向量表没有什么关系啊,难道这个内部SRAM有自动拷贝到内存初始地址的功能?在三星手册没有啊?哈哈,真是不知道怎么弄的了。
        希望高人再帮我解决疑惑了。
        二、ADS 的MMU初始化和eboot中的有点小小的差异。
        在ADS中
        void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
{
    U32 *pTT;
    int i,nSec;
    pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
    nSec=(vaddrEnd>>20)-(vaddrStart>>20);
    for(i=0;i <=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i) < <20);
}

这里加的偏移地址是(vaddrStart>>20)虚拟地址右移了20位,但是eboot中却是右移了18位,其余的基本一致,请问这是怎么回事。
我看了一下ARM 结构体系相关的英文文档,发现右移20位才合适啊。eboot中咋了,但是确实能正常使用。这点也请高人指示,我想
掌握了ARM中断向量的运用,那我觉得对ARM的认识才算入门了。
        哈哈,今天本来公司安排弄摄像头的,可是我一直想着这件事情,就看了一下,结果看了好久才了解一点。总算有点收获了。
       

回复评论 (41)

强帖!!!
请教什么时候需要用CACHE什么时候需要用UNCACHE???
点赞  2009-6-19 16:10
嘿嘿,刚才http://www.armce.cn/bbs的老大斑竹帮我解决了
问题二。

这是指针和非指针的问题。

和下面的效果一样。

ETDrawer回复我:
pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20); 我感觉可能是这个原因

DWORD A = 0x12345678;
DWORD dwAddr = A + 8;

PDWORD B = 0x12345678;
PDWORD pdwAddr = B + 2;

dwAddr == (DWORD)pdwAddr?
点赞  2009-6-19 16:22
引用: 引用 1 楼 wangxin_801115 的回复:
强帖!!!
请教什么时候需要用CACHE什么时候需要用UNCACHE???


cache 是高速缓存,用来存储不常变更的数据。

如果经常改变的不能使用cache,比如外设。IO等。

在这里的中断就使用了cache地址。
点赞  2009-6-19 16:23
mark, 学习wince中。
点赞  2009-6-19 17:27
  这里要提醒的是在eboot中发生的中断时跳到STEPLDR存放中断向量表的地方的,以前我居然认为是跳到eboot中断向量表的地方,真是雷死人了。
——哈哈,关于问题一,源于我对上面的错误理解。现在我纠正!
eboot发生中断,就会跳转到虚拟地址0,这个虚拟地址0对应的物理地址就是存放中断向量的正确地方。

所以在#define pISR (*(unsigned *)(0x30000000/0x00000000/0x8C000000+0x18))

都是可以的,因为它指向同一个物理地址0x30000000
这个中断向量式隐含的初始化了,并且在eboot中要留有空间。


OK所有问题都解决了!哈哈

点赞  2009-6-20 00:13
哈哈,我觉得paul chao牛人太厉害了,崇拜死他了。
点赞  2009-6-20 00:15
0x30000000/0x00000000/0x8C000000+0x18   

这个语法是什么意思哦  难道这三个地址对应的是一个地方。想不明白也

gooogleman指点下下


点赞  2009-6-21 09:45
引用: 引用 7 楼 changjiesun 的回复:
0x30000000/0x00000000/0x8C000000+0x18  

这个语法是什么意思哦  难道这三个地址对应的是一个地方。想不明白也

gooogleman指点下下

看上面的MMU初始化啊。对应同一个物理地址!
点赞  2009-6-21 10:30
引用: 引用 6 楼 gooogleman 的回复:
哈哈,我觉得paul chao牛人太厉害了,崇拜死他了。

paul chao曾经说过,MMU开时,中断往虚拟地址(0x00000000-0x0000001c)跳转,MMU关时,中断往物理地址(0x00000000-0x0000001c)跳转
点赞  2009-6-21 10:39
引用: 引用 9 楼 xingxing_y 的回复:
引用 6 楼 gooogleman 的回复:
哈哈,我觉得paul chao牛人太厉害了,崇拜死他了。


paul chao曾经说过,MMU开时,中断往虚拟地址(0x00000000-0x0000001c)跳转,MMU关时,中断往物理地址(0x00000000-0x0000001c)跳转


有理。就是这么回事。
点赞  2009-6-21 14:52

googleman的认真回复-感动
paul chao的技术水平-佩服

eeworld真是藏龙卧虎,呵呵,感谢感谢
点赞  2009-6-22 10:21
引用: 引用 11 楼 yangyong9608 的回复:

googleman的认真回复-感动
paul chao的技术水平-佩服

eeworld真是藏龙卧虎,呵呵,感谢感谢


因为我自己以前也没有真正明白这个基础,真是惭愧,抓住这个机会学习一把。

问题解决,接分吧。
点赞  2009-6-22 10:31
当时xy1001跟我在qq里发了堆栈初始化的代码,
我在eboot的fw.s(也就是kernel/hal/arm下的fw.s)里

resethandler函数中加入了
        mrs                r0, cpsr
        bic                r0, r0, #MODEMASK
        orr                r1, r0, #IRQMODE|NOINT
        msr                cpsr_cxsf, r1                ; IRQMode
        ldr                sp, = IRQStack
       
        bic                r0, r0, #MODEMASK|NOINT
        orr                r1, r0, #SVCMODE
        msr                cpsr_cxsf, r1                ; SVCMode.
        ldr                sp, = SVCStack
        ;;;
代码

#define pISR                (*(unsigned *)(0x00000000+0x18))

中断还是没触发,呵呵,找来找去找不到原因,只好放弃了
点赞  2009-6-22 10:34
不知道我的理解对不对:

而2440 此时MMU已经打开,不能识别0x30000000物理地址,一旦中断,就会跳转到0x00000000这个虚拟地址去执行相应的中断handler
(*^__^*) 嘻嘻……,我是这么理解的,不知道对不对,这个问题已经困扰我好久了,以前似懂非懂,现在我觉得这应该是正确的。


意思就是说arm运行的时候走的是虚拟地址,虚拟地址映射到哪里,那么arm实际就到哪里取指。

在wince下,中断时候就是跳转到0xffff0000地方,我看过下面的代码,确实如此。所以ARM相关文档中0xffff0000以及0x00000000这两个
存放中断向量表的地方应该指的是虚拟地址。


arm没加wince之前,来中断的时候跳转到0x00000000+中断地址,这应该是arm硬件默认的。至于加了wince之后,中断跳转到0xffff0000,这应该是软件映射的吧,它是怎么做到arm来中断时,跳转到这里的呢?

那么,在PB下的2440 eboot+STEPLDR中也应该是一样的。
经过上面的层层映射,0x00000000、0x8C000000以及0x30000000这三个虚拟地址都同时指向物理地址0x30000000,所以这三个虚拟地址
可以互相代替,也是一样的效果,这恰恰证明了MMU的高明——多个虚拟地址可以对应一个物理地址!

这里和我想的一样。

不过,在这里我还是有两个疑问。
一、STEPLDR的代码是在2440 的内部4K SRAM 运行的,那中断向量表应该还是在0x0000 0000处才对,难道这时候2440 就把0x0000 0000
看成是虚拟地址了?0x00000000、0x8C000000以及0x30000000这三个虚拟地址都同时指向物理地址0x30000000,但是0x30000000好像和
中断向量表没有什么关系啊,难道这个内部SRAM有自动拷贝到内存初始地址的功能?在三星手册没有啊?哈哈,真是不知道怎么弄的了。
希望高人再帮我解决疑惑了。


这个时候mmu还没有开,物理地址和虚拟地址是一一对应的。用你前面的理论,arm依旧走虚拟地址,只不过STEPLDR这段代码应该把所有中断都关了吧(我没看代码),所以应该不会响应中断。2440我没具体研究过sram,不过2410的sram是映射在0x40000000的位置(没开中断的时候虚实统一),三星的启动代码STEPLDR应该都是小于4K吧,因为如果采用nandfalsh启动,开机以后,arm在硬件上第一件事就是将nandfalsh的前4K程序拷到sram里面,然后执行的。所以前4K代码必须包含拷贝后面程序的操作。开机之后,前4K代码在三星的arm硬件上是自动拷贝到sram的。

二、ADS 的MMU初始化和eboot中的有点小小的差异。
在ADS中
void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
{
    U32 *pTT;
    int i,nSec;
    pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
    nSec=(vaddrEnd>>20)-(vaddrStart>>20);
    for(i=0;i <=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i) < <20);
}

这里加的偏移地址是(vaddrStart>>20)虚拟地址右移了20位,但是eboot中却是右移了18位,其余的基本一致,请问这是怎么回事。
我看了一下ARM 结构体系相关的英文文档,发现右移20位才合适啊。eboot中咋了,但是确实能正常使用。这点也请高人指示,我想
掌握了ARM中断向量的运用,那我觉得对ARM的认识才算入门了。


不知道我的理解对不对:
你看一下硬件设计,norflash的地址线和sdram的地址线,有点区别。
我的2410的norflash软件设计成了16位(可以是8或16),sdram可以是16位也可以是32位的,eboot我现在为止还没有研究,如果它是运行在32位环境下,那么20和18就可以解释了。是为了地址对齐,才做的20或18的移位。
点赞  2009-6-22 11:28
不知道我的理解对不对:
你看一下硬件设计,norflash的地址线和sdram的地址线,有点区别。

不管这里的事,呵呵,我想错了,这里的区别是为了访问硬件用的。

20和18仅仅是为了32位和16位的指令寻址而用的。
点赞  2009-6-22 11:32
引用: 引用 14 楼 CBEMA 的回复:
不知道我的理解对不对:
你看一下硬件设计,norflash的地址线和sdram的地址线,有点区别。
我的2410的norflash软件设计成了16位(可以是8或16),sdram可以是16位也可以是32位的,eboot我现在为止还没有研究,如果它是运行在32位环境下,那么20和18就可以解释了。是为了地址对齐,才做的20或18的移位。


我没有看过Nor 的代码,不知道,不过我觉得ARM是32位的机器,使用32位的速度是比16位访问时要快的。我想NOR虽然硬件上是16位,但是访问的时候还是以32位来访问的吧,只不过放在低16位而已。
点赞  2009-6-22 11:35
你看一下总线宽度和等待控制寄存器(BWSCON)。

[29:28] 决定bank7 的数据总线宽度
00=8 位,01=16 位,10=32 位,11=保留


bank7在此是sram(2410里是这样的,你看看2440是什么样的)。这里如果设置成01,那么它就是16位访问机制。STEPLDR是在sram里执行的。



你再看看sdram(bank6 )的
[25:24] 决定bank6 的数据总线宽度
00=8 位,01=16 位,10=32 位,11 保留

eboot是在sdram里面执行的,你看看这里设置的是16还是32。

程序采用16还是32去执行,首先取决于这里。

所以移位18或20,以保证访问的是2字节或4字节的倍数。

点赞  2009-6-22 11:55
googleman,也帮忙看下我的问题啊,电源管理方面的
点赞  2009-6-22 16:23
引用: 引用 18 楼 daigua04 的回复:
googleman,也帮忙看下我的问题啊,电源管理方面的

你的PXA270 我没有做过,不敢发表意见哦。慢慢看,总能解决的。
点赞  2009-6-22 16:54
123下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复