zoukankan      html  css  js  c++  java
  • eboot中断向量问题(作者:wogoyixikexie@gliet)

    eboot中断向量问题(作者:wogoyixikexie@gliet)

    //-----------------------------------------------------------------------------------------------------------

    // 作者:wogoyixikexie@gliet

    //论坛账号:gooogleman (经常在CSDN出没)

    // 版权:桂林电子科技大学一系科协wogoyixikexie@gliet

    // 平台:wince5.0 2440 5.0 BSP

    // 发布日期:2009年8月10日

    // 最后修改:

    // 注意事项:未经作者同意,不得在转载的时候擅自修改、删除文章的任何部分

    //-----------------------------------------------------------------------------------------------------------


          前几天这个帖子http://topic.csdn.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.csdn.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的认识才算入门了。
    哈哈,今天本来公司安排弄摄像头的,可是我一直想着这件事情,就看了一下,结果看了好久才了解一点。总算有点收获了。

    ————————————————————————————————————————————————————————

    嘿嘿,刚才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?
      
    ——————————————————————————————
     
    这里要提醒的是在eboot中发生的中断时跳到STEPLDR存放中断向量表的地方的,以前我居然认为是跳到eboot中断向量表的地方,真是雷死人了。
    ——哈哈,关于问题一,源于我对上面的错误理解。现在我纠正!
    eboot发生中断,就会跳转到虚拟地址0,这个虚拟地址0对应的物理地址就是存放中断向量的正确地方。

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

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


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


    更多问题,请看http://topic.csdn.net/u/20090619/15/fba7c409-371c-40ec-80db-4e2ccfafc5d9.html帖子
  • 相关阅读:
    解决:transform-decorators-legacy 报错
    leetcode刷题笔记 232题 用栈实现队列
    leetcode刷题笔记 231题 2的幂
    leetcode刷题笔记 230题 二叉搜索树中第K小的元素
    leetcode刷题笔记 229题 求众数II
    leetcode刷题笔记 228题 汇总区间
    leetcode刷题笔记 227题 基本计算器II
    leetcode刷题笔记 225题 用队列实现栈
    leetcode刷题笔记 224题 基本计算器
    leetcode刷题笔记 223题 矩形面积
  • 原文地址:https://www.cnblogs.com/gooogleman/p/1869631.html
Copyright © 2011-2022 走看看