yiffer的个人空间 https://blog.eetop.cn/edesign [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

arm linux 从入口到start_kernel 代码详细分析6

已有 1631 次阅读| 2010-10-7 15:40 |个人分类:Linux移植

5. 开启mmu
        开启mmu是又函数 __enable_mmu 实现的.
      
        在进入 __enable_mmu 的时候, r0中已经存放了控制寄存器c1的一些配置(在上一步中进行的设置), 但是并没有真正的打开mmu,
        在 __enable_mmu 中,我们将打开mmu.
      
        此时,一些特定寄存器的值如下所示:
r0 = c1 parameters      (用来配置控制寄存器的参数)      
r4 = pgtbl              (page table 的物理基地址)
r8 = machine info       (struct machine_desc的基地址)
r9 = cpu id             (通过cp15协处理器获得的cpu id)
r10 = procinfo          (struct proc_info_list的基地址)
      
        在 arch/arm/kernel/head.S 中:

00146:  .type __enable_mmu, %function
00147: __enable_mmu:
00148: #ifdef CONFIG_ALIGNMENT_TRAP
00149:  orr r0, r0, #CR_A
00150: #else
00151:  bic r0, r0, #CR_A
00152: #endif
00153: #ifdef CONFIG_CPU_DCACHE_DISABLE
00154:  bic r0, r0, #CR_C
00155: #endif
00156: #ifdef CONFIG_CPU_BPREDICT_DISABLE
00157:  bic r0, r0, #CR_Z
00158: #endif
00159: #ifdef CONFIG_CPU_ICACHE_DISABLE
00160:  bic r0, r0, #CR_I
00161: #endif
00162:  mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
00163:         domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
00164:         domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
00165:         domain_val(DOMAIN_IO, DOMAIN_CLIENT))
00166:  mcr p15, 0, r5, c3, c0, 0  @ load domain access register
00167:  mcr p15, 0, r4, c2, c0, 0  @ load page table pointer
00168:  b __turn_mmu_on
00169:
00170:
00181:  .align 5
00182:  .type __turn_mmu_on, %function
00183: __turn_mmu_on:
00184:  mov r0, r0
00185:  mcr p15, 0, r0, c1, c0, 0  @ write control reg
00186:  mrc p15, 0, r3, c0, c0, 0  @ read id reg
00187:  mov r3, r3
00188:  mov r3, r3
00189:  mov pc, r13
      
第146, 147行: 函数声明
第148 - 161行:  根据相应的配置,设置r0中的相应的Bit. (r0 将用来配置控制寄存器c1)
第162 - 165行: 设置 domain 参数r5.(r5 将用来配置domain)
第166行: 配置 domain (详细信息清参考arm相关手册)
第167行: 配置页表在存储器中的位置(set ttb).这里页表的基地址是r4, 通过写cp15的c2寄存器来设置页表基地址.

arm linux 从入口到start_kernel 代码分析
第168行: 跳转到 __turn_mmu_on. 从名称我们可以猜到,下面是要真正打开mmu了.
        (继续向下看,我们会发现,__turn_mmu_on就下当前代码的下方,为什么要跳转一下呢? 这是有原因的. go on)
第169 - 180行: 空行和注释. 这里的注释我们可以看到, r0是cp15控制寄存器的内容, r13存储了完成后需要跳转的虚拟地址(因为完成后mmu已经打开了,都是虚拟地址了).

第181行: .algin 5 这句是cache line对齐. 我们可以看到下面一行就是 __turn_mmu_on, 之所以
第182 - 183行:  __turn_mmu_on 的函数声明. 这里我们可以看到, __turn_mmu_on 是紧接着上面第168行的跳转指令的,只是中间在第181行多了一个cache line对齐.
        这么做的原因是: 下面我们要进行真正的打开mmu操作了, 我们要把打开mmu的操作放到一个单独的cache line上. 而在之前的"启动条件"一节我们说了,I Cache是可以打开也可以关闭的,这里这么做的原因是要保证在I Cache打开的时候,打开mmu的操作也能正常执行.
第184行: 这是一个空操作,相当于nop. 在arm中,nop操作经常用指令 mov rd, rd 来实现.
        注意: 为什么这里要有一个nop,我思考了很长时间,这里是我的猜测,可能不是正确的:
        因为之前设置了页表基地址(set ttb),到下一行(185行)打开mmu操作,中间的指令序列是这样的:
        set ttb(第167行)
        branch(第168行)
        nop(第184行)
        enable mmu(第185行)
        对于arm的五级流水线: fetch - decode - execute - memory - write
      
        他们执行的情况如下图所示:
      
            +------------+---+---+---+---+---+---+---+---+      
            |  set ttb   | F | D | E | M | W |   |   |   |      
            +------------+---+---+---+---+---+---+---+---+      
            |  branch    |   | F | D | E |   |   |   |   |      
            +------------+---+---+---+---+---+---+---+---+      
            |   nop      |   |   |   |   | F | D |   |   |      
            +------------+---+---+---+---+---+---+---+---+      
            | enable mmu |   |   |   |   |   | F |   |   |      
            +------------+---+---+---+---+---+---+---+---+      
                                                                
                                                                
             F - fetch                                          
             D - Decode                                         
             E - Execute                                        
             M - Memory                                         
             W - Write Register                                 
                                                                
        这里需要说明的是,branch操作会在3个cycle中完成,并且会导致重新取指.
      
        从这个图我们可以看出来,在enable mmu操作取指的时候, set ttb操作刚好完成.
   
      
第185行: 写cp15的控制寄存器c1, 这里是打开mmu的操作,同时会打开cache等(根据r0相应的配置)
第186行: 读取id寄存器.
第187 - 188行: 两个nop.
第189行: 取r13到pc中,我们前面已经看到了, r13中存储的是 __switch_data (在 arch/arm/kernel/head.S 91行),下面会跳到 __switch_data.

第187,188行的两个nop是非常重要的,因为在185行打开mmu操作之后,要等到3个cycle之后才会生效,这和arm的流水线有关系.
因而,在打开mmu操作之后的加了两个nop操作.

本文来自: (www.91linux.com) 详细出处参考:http://www.91linux.com/html/article/qianrushiyingyong/arm_linux/2010/0909/20471.html


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 20

    获赞
  • 69

    评论
  • 3705

    访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-26 17:36 , Processed in 0.017466 second(s), 7 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部