gooogleman

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

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

// 作者: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帖子
 
posted on 2009-08-10 09:35  gooogleman.烟雨漓江  阅读(570)  评论(1编辑  收藏  举报