1. PPC e500ms框图

2. PPC Memory Map知识

这里指内存及各种外设组件在CPU地址空间的map.

  • LAW寄存器组: 共32个. 负责各组件在36位物理地址空间内的排布 组件ID是预定义的, 有点像mac地址, 代表组件在SOC级的bus中, 传输"地址寻址"的事务. 传输事务有源和目的. 有的组件可以是源, 也可以是目的. 有的组件只能是其中一种. 能做源的组件, 都可以主动发起访问, 比如CPU, 和PCIe以及DMA控制器. 主要ID有:
Source/Target ID Transaction source Transaction target Notes
0x00 PCI-Express 1 PCI-Express 1
0x0F Reserved Local Space
0x10 Reserved Memory Complex 1 DDR controller 1 or CPC1 SRAM
0x18 Buffer Manager (control) Buffer Manager Software Portal
0x1F Reserved eLBC
0x3C Queue Manager (control) Queue Manager Software Portal
0x48 Pre-boot loader (PBL) Reserved
0x70 DMA 1 Reserved
0x80 Core 0 (instruction) Reserved
0x81 Core 0 (data) Reserved
0xC0 Frame Manager 1 ID 1 Reserved

LAW不负责虚拟地址到物理地址的转换, 它只是负责物理地址的重新排布.

  • 典型的物理地址布局

  • CCSRBAR负责映射soc寄存器基地址

  • boot space 转换 每个核在复位的时候, 都有个4K的MMU映射在0x0_FFFF_F000, 并且从物理地址0x0_FFFF_FFFC执行代码. 意思是, 在复位的时候, MMU有个默认的1:1 4K映射. 第一条取指从地址0x0_FFFF_FFE0开始(burst read). boot space 转换的作用是提供映射不同的组件到0x0_FFFF_F000 如果一个地址落在boot window(8 Mbytes at 0x0_FF80_0000 to 0x0_FFFF_FFFF)里面, boot space转换就生效(但需要enable), 这三个寄存器控制其map到哪里: BSTRH, BSTRL, and BSTAR, 转换到4GB-BSTAR[SIZE] to 4GB-1

3. 虚实地址转换与寄存器知识

  • MSR
  • L1 MMU也有tlb, 但对软件不可见; 硬件把L1的TLB作为L2的TBL的缓存(inclusive cache), 自动管理.
  • 两类TLB: TLB0和TLB0, 是并行工作的; TLB0的page size是固定4K, 比TLB1少了些通用性, 但entry数多. 多4K page的kernel应该比较友好.
  • 多个tlb hit到一个虚拟地址是错误的.这种情况下, tlb会返回无效地址, 并且产生machine check

虚拟地址转换为物理地址过程:

tlb匹配过程:

tlb的标记位含义:



4. 主核linux启动

4.1. uboot阶段

uboot启动到命令行后, 主核tlb如下:
其代码运行在0x1ff0944c范围

idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
  1 0 0x00000000 --   0V--   a  0-----UrUwUxSrSwSx -> 0x1:00000000 --------------Iprot
  2 0 0x40000000 --   0V--   a  0-----UrUwUxSrSwSx -> 0x1:40000000 --------------Iprot
  5 0 0xff800000 --   0V--   6  0-I-G-------SrSwSx -> 0xf:ff800000 --------------Iprot
  6 0 0xffc00000 --   0V--   6  0-I-G-------SrSwSx -> 0xf:ffc00000 --------------Iprot
  8 0 0xff200000 --   0V--   5  0-I-G-------SrSw-- -> 0xf:ff200000 --------------Iprot
  9 0 0xf4000000 --   0V--   5  0-----------SrSw-- -> 0xf:f4000000 --------------Iprot
 10 0 0xf4100000 --   0V--   5  0-I-G-------SrSw-- -> 0xf:f4100000 --------------Iprot
 11 0 0xf4200000 --   0V--   5  0-----------SrSw-- -> 0xf:f4200000 --------------Iprot
 12 0 0xf4300000 --   0V--   5  0-I-G-------SrSw-- -> 0xf:f4300000 --------------Iprot
 13 0 0xf0000000 --   0V--   6  0-I-G-------SrSw-- -> 0xf:00000000 --------------Iprot
 16 0 0x80000000 --   0V--   9  0-I-G-------SrSw-- -> 0xf:80000000 --------------Iprot
 17 0 0x90000000 --   0V--   9  0-I-G-------SrSw-- -> 0xf:90000000 --------------Iprot
 18 0 0xff300000 --   0V--   4  0-I-G-------SrSw-- -> 0xf:ff300000 --------------Iprot
 19 0 0xeffff000 --   0V--   1  0-I-G-------SrSwSx -> 0x1:1ffff000 --------------Iprot
 37 0 0xfe000000 --   0V--   7  0-I-G-------SrSw-- -> 0xf:fe000000 --------------Iprot
 38 0 0xff000000 --   0V--   2  0-I-G-------SrSw-- -> 0xf:ff000000 --------------Iprot
 39 0 0xff004000 --   0V--   2  0-I-G-------SrSw-- -> 0xf:ff004000 --------------Iprot

从这里开始, uboot开始load linux.itb并执行.
linux.itb包括uImage, fdt, rootfs
uImage也是个itb, 是个压缩过的linux二进制. 不用解析elf.

Created with Raphaël 2.1.4uboot_wrappers.cuboot_wrappers.cfant/board.cfant/board.ccommon/cmd_bootm.ccommon/cmd_bootm.cpowerpc/lib/bootm.cpowerpc/lib/bootm.cprepare_images()fdt addr 0x... //initrd isam mchosen 0xc10000 0x9d61c8;fdt boa//做dtb的fixup ft_board_setup//要非零物理地址启动, base要是实际LAW配的物理地址 注1: fdt_fixup_memory(base, size)bootm 0x7a0002a4 - 0x11000000bootm_find_os boot_get_kernel//for dts bootm_find_otherbootm_load_osboot_relocate_fdtdo_bootm_linuxboot_prep_linux boot_body_linux boot_jump_linux注2: kernel = 把images->ep强转为函数指针; 注3: (*kernel) ((bd_t *)of_flat_tree, 0, 0, EPAPR_MAGIC, getenv_bootm_mapsize(), 0, 0)

  • 注1:
    在fant上, ddr被配置在物理地址4G以上. 而uboot默认base为0, 不修改会导致kernel无法启动.
    对DDR来说, 它的ID是0x10和0x11, 它不可能是一个memory transaction的源, 只可能是目的.
    LAW寄存器: ddr使用fe00_0c70和fe00_0c80, 被map到36位物理地址0x1:00000000, 两段各2G, 一共4G.
    具体LAW寄存器要看芯片手册第二章, memory map

  • 注2:
    在uboot解析uImage的时候, 它知道:
    Load Address: 00000000
    Entry Point: 00000000
    注意这个Entry Point就是uboot要跳转到kernel的地址

  • 注3:
    对kernel来说, 第一条命令在0xc0000000
    对uboot来说, kernel被load后, 第一条命令在0x00000000; 如果要用仿真器调试kernel, 第一个断点应打在0x00000000
    uboot跳转到0地址, 后面的所有都是kernel负责, 包括配kernel自己的tlb

4.2. kernel阶段

vmlinux的链接地址是0xc000_0000, 被uboot load到物理地址0x1:00000000.
vmlinux使用uboot配的tlb来运行kernel的第一条指令, 该指令位于head.S
在这条TLB中, 物理地址0x1:00000000被映射到虚拟地址0

idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
  1 0 0x00000000 --   0V--   a  0-----UrUwUxSrSwSx -> 0x1:00000000 --------------Iprot

所以, 可以说kernel是从虚拟地址0开始执行的. kernel在执行过程中, 用tlbsx命令, 能根据虚拟地址, 查到实际运行的物理地址, 并以此来配置TLB.
当前的虚拟地址, 可以用跳转指令bl, 然后读lr寄存器得到.如:

    bl  invstr
invstr:
    mflr    r6

kernel启动流程如下:

Created with Raphaël 2.1.4init/main.cinit/main.chead_fsl_booke.Shead_fsl_booke.Spowerpc/kernel/setup_32.cpowerpc/kernel/setup_32.cpowerpc/kernel/prom.cpowerpc/kernel/prom.cpowerpc/mm/init_32.cpowerpc/mm/init_32.cpowerpc/mm/fsl_booke_mmu.cpowerpc/mm/fsl_booke_mmu.cc0000000 <_start>: 此时还在用uboot配的TLB, 虚拟地址从0开始invalidate所有uboot的TLB, 但保留当前运行的kernel代码所用的TLB.注1: //建立第一条kernel的TLB__early_start: include fsl_booke_entry_mapping.S到这里已经是0xC000_0000地址了使用特殊指令mtivor0 .. mtivor15建立中断表: set_ivor如果是从核, 跳转到 __secondary_start建立第一个线程的ptr到init_task,建立栈跳转到第一个C函数 early_init清零bss, 识别CPU类型machine_init到这里kernel已经被relocated好了early_init_devtree根据device treee, 获取initrd等信息建立MEMBLOCKs: 内存区间表扫描并解析dtearly_init_mmu()MMU_init从命令行解析MMU配置 reserve hugetlbadjust_total_lowmem为kernel固定768M "low memory"映射 注2: map_mem_in_cams_addr(物理地址,0xc0000000,768,3,0)初始化MMU硬件start_kernel()lockdep_init();cgroup_init_early();local_irq_disable();boot_cpu_init();page_address_init();打印Linux versionsetup_arch(&command_line);初始化mm, percpu, softirq, 页表分配器, 打印并解析命令行建立log buf, pid hash表, vfs的cache trap_init(); mm_init();sched_init();rcu_init();tick_nohz_init();context_tracking_init();radix_tree_init();init_IRQ();tick_init();init_timers();softirq_init();time_init();perf_event_init();call_function_init();开中断local_irq_enable();开始有中断初始化page_cgroup numa_policy sched_clock pidmap anon_vma thread_info_cache signal 等等...初始化cgroup cpuset ftracerest_init(): 开始第一个线程:kernel_init和第二个线程kthreadd到这里所有初始化结束, 进入idlewhile(1) do_idle()

  • 注1:
    第一个64M TLB, 映射kernel代码段的. 从0xc0000000映射到4G物理地址

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xc0000000 --   0V--   8  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    
  • 注2:
    用TLB1中的3个TLB, 固定映射kernel的768M空间
    这里物理地址是从全局变量 memstart_addr得到的, 而这个全局变量是由dtb解析来的.
    所谓kernel的lowmem就是0xc0000000开始的768M, PPC用3条TLB来做固定映射. kmalloc可以直接从这段区域直接申请内存. 看起来应该是这样: 0xc0000000, 0xd0000000, 0xe0000000各256M

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xc0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    1 0 0xd0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:10000000 --------------Iprot
    2 0 0xe0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:20000000 --------------Iprot
    

    这要求dts要修改base到4G

    memory {
          //从4G开始, 大小4G
          reg = <0x00000001 0x00000000 0x00000001 0x00000000>;
          device_type = "memory";
    }
    

5. 从核linux启动

从核也是从0xFFFF_FFFC启动. 但会等待主核kick off

5.1. uboot阶段

uboot启动从核流程如下:

Created with Raphaël 2.1.4主流程 @主核主流程 @主核mpc85xx/fdt.c @主核mpc85xx/fdt.c @主核mpc85xx/mp.c @主核mpc85xx/mp.c @主核mpc85xx/release.S @所有从核mpc85xx/release.S @所有从核省略前面过程, 此时uboot已经在ram中运行setup_mp()boot page是最大DDR地址-4K 注0: 找到__second_half_boot_page的物理地址 找到spin table的物理地址注1: 找到虚拟地址CONFIG_BPTR_VIRT_ADDR的tlb index重新配置这个TLB index, 使得CONFIG_BPTR_VIRT_ADDR map到boot page拷贝__secondary_start_page到CONFIG_BPTR_VIRT_ADDR, 即拷贝到boot page的物理地址注2: plat_mp_up(): 把boot page的地址写入bstrh和bstrl寄存器release 从核注3: 0xFFFF_FFFC: __secondary_reset_vector: b __secondary_start_page0xFFFF_F000: __secondary_start_pageCPU core初始化, errata修复, 打开CPU cache, 分支预测等组件.配置TLB index5, 映射CCSR. 注4:解析spin table地址, 建立spin table的TLB映射 注5:TLB更新从__bootpg_addr取__second_half_boot_page的物理地址, rfi(return from interrupt)跳转到这个地址 这个地址在上面刚刚配置的TLB内.注6: __second_half_boot_page: 建立spin table entry置位spin table中本CPU的标记循环等待spin table被填入addrPC地址: 0x1fee4048等待spin table中每个CPU的标记, 1表示对应的从核已经跑到主核继续运行"boot m"命令ft_fixup_cpu在dtb中添加spin table信息:获取spin table总地址在每个cpu节点下面:"enable-method" = "spin-table""cpu-release-addr"=该cpu的spin table物理地址并把相关内存添加到reserve memory

  • 注0:
    __secondary_start_page的编译地址是: 0x0ff43000
    __second_half_boot_page的编译地址是:0x0ff44000
    它们是挨着的两个4K空间.
    __spin_table_addr在第1个4K: 0x0ff43284, 这是个指针, 编译时为0
    __spin_table在第2个4K: 0x0ff44204

  • 注1:
    CONFIG_BPTR_VIRT_ADDR默认是0xfffff000, 但fant改写为0xeffff000

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    19 0 0xeffff000 --   0V--   1  0-I-G-------SrSwSx -> 0x1:1ffff000 --------------Iprot
    
  • 注2:
    从核也是从0xFFFF_FFFC启动. 设置bstr寄存器后, 0xFFFF_F000开始的4K会被映射到bstr寄存器指向的地址, 即主核设置的boot page; boot page包含release.S中的__secondary_start_page开始的4K代码, 这4K代码的最后一句正好是0xFFFF_FFFC: b __secondary_start_page, 从核从这句开始执行.

  • 注3:
    从核在复位状态下的默认TLB为:

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xfffff000 --   0V--   1  0-I---------SrSwSx -> 0x0:fffff000 --------------Iprot
    

    这里的paddr只是从核认为的物理地址, 实际上是bstr寄存器指向的地址.

  • 注4:
    配好CCSR后, TLB:

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xfffff000 --   0V--   1  0-I---------SrSwSx -> 0x0:fffff000 --------------Iprot
    5 0 0xfe000000 --   0V--   1  0-I-G-------SrSw-- -> 0xf:fe000000 --------------Iprot
    
  • 注5:
    __spin_table_addr指向的地址, 找到spin table地址; __spin_table_addr在第1个4K: 0x0ff43284
    所以在reset地址来看, 是0xfffff284, 此时它的内容是1fee4100, 应该是__spin_table在ram里relocate后的地址.
    这是个差点到512M的物理地址.
    此时TLB为:

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xfffff000 --   0V--   1  0-I---------SrSwSx -> 0x0:fffff000 --------------Iprot
    1 0 0x1fee4000 --   0VTs   1  0--MG-------SrSwSx -> 0x1:1fee4000 --------------Iprot
    5 0 0xfe000000 --   0V--   1  0-I-G-------SrSw-- -> 0xf:fe000000 --------------Iprot
    
  • 注6:
    这里已经不是boot space范畴了. __second_half_boot_page在普通的ram中. 前面的汇编打开了cache, 所以这段代码是可以被cache的. spin table定义如下:

    struct {
      uint64_t entry_addr;
      uint64_t r3;
      uint32_t rsvd1;
      uint32_t pir;
      ...占位,64字节, 一个cacheline
    }
    

    总的spin table在0x1fee4100(见上), 每个CPU占64字节(0x40).
    r10寄存器指向本CPU在spin table的entry. 这里我们调试的是CPU2, 此时r10是0x1fee4180

    ppcP3041[2,s] % md 0x1fee4180
    0x1fee4180:
    1fee_4180   00000000 00000001 00000000 00000002              ................
    1fee_4190   00000000 00000002 00000000 00000000              ................
    1fee_41a0   00000000 00000000 00000000 00000000              ................
    1fee_41b0   00000000 00000000 00000000 00000000              ................
    

5.2. linux阶段

linux启动后, 在/sys/firmware/devicetree/base下能看到相关的dtb配置.
比如对CPU2来说, 它的spin table物理地址是0000 0001 1fee 4180, 和上面分析是一致的.

/sys/firmware/devicetree/base/cpus/PowerPC,e500mc@2 # hexdump -C enable-method
00000000  73 70 69 6e 2d 74 61 62  6c 65 00                 |spin-table.|
0000000b
/sys/firmware/devicetree/base/cpus/PowerPC,e500mc@2 # hexdump  cpu-release-addr
0000000 0000 0001 1fee 4180                    
0000008

Created with Raphaël 2.1.4第一个进程kernel_init @主核第一个进程kernel_init @主核kernel/smp.c @主核kernel/smp.c @主核kernel/cpu.c @主核kernel/cpu.c @主核powerpc/kernel/smp.c @主核powerpc/kernel/smp.c @主核...初始化多核: do_pre_smp_initcalls();lockup_detector_init();smp_init();sched_init_smp();每个CPU都有idle threads struct task_struct *idle_threads[NR_CPUS];smp_initfor each cpu: cpu_up(cpu)cpu_up获取该CPU的idle = idle thread 为这个CPU新建线程__cpu_up(cpu, idle)全局变量unsigned int cpu_callin_map[NR_CPUS];初始化idle进程的thread_infokick_cpu激活这个CPU的线程 通知这个CPU已经online打印从核已启动 Brought up 4 CPUsdo_basic_setup(): 初始化共享内存 驱动 irq 系统调用 执行initcalls从ramdis加载默认ko至此系统正常running: 依次尝试执行/init /sbin/init /etc/init /bin/init /bin/sh

Created with Raphaël 2.1.4powerpc/kernel/smp.c @主核powerpc/kernel/smp.c @主核platforms/85xx/smp.c @主核platforms/85xx/smp.c @主核mpc85xx/release.S @从核mpc85xx/release.S @从核head_fsl_booke.S @从核head_fsl_booke.S @从核powerpc/kernel/smp.c @从核powerpc/kernel/smp.c @从核循环等待spin table被填入addrPC地址: 0x1fee4048此时还在uboot代码里面smp_85xx_kick_cpu从device tree获取cpu-release-addr例如CPU2: *cpu_rel_addr=0x11fee4180将该地址ioremap得到spin table虚拟地址注1: 将__early_start的物理地址写入spin table从核启动物理地址生效:0x10000009c等待从核release注2: mask spin table 状态为released打开本地中断注3: 为该启动地址配置64M的1:1TLBTLB配好后, rfi命令跳转到启动地址rfi命令跳转到linux代码//建立第一条kernel的TLB__early_start: include "fsl_booke_entry_mapping.S"这部分和主核一样注4: 虚拟地址从0xc000_0000开始了设置中断向量表从核, 跳转到 __secondary_start注5: 从核直接从TLBCAM[index] load tlb配置使低768M内存常驻TLB配置初始current和current_thread_info到全局变量建立栈跳转到start_secondarystart_secondarycpu_callin_map[cpu]置位等待从核给cpu_callin_map[CPU]置位 timeout说明从核没起来通知主核, 启动成功初始化cputime配置numacpu上线打开本地中断注6: 从核从IDLE开始运行cpu_startup_entry(CPUHP_AP_ONLINE_IDLE)

  • 注1:
    在linux下面, spin table定义如下:

    struct epapr_spin_table {
      u32    addr_h;
      u32    addr_l;
      u32    r3_h;
      u32    r3_l;
      u32    reserved;
      u32    pir;
    };
    

    这里的kernel代码有个bug, CPU是PPC32时, kernel不写addr_h, 一般系统的DDR配置在物理0地址, addr_h不写也没问题.
    但fant的物理地址在0x1_0000_0000, 即从4G开始, 那么__pa(__early_start)是大于32位的, 不写addr_h会导致从核得到的启动地址不对, 从核无法启动. 在fant上的相关地址打印如下: 注for注1:

    cpu_rel_addr=0xc3fdc4ac *cpu_rel_addr=0x11fee4180
    high_memory=0xf0000000 virt_to_phys(high_memory)=0x30000000
    __early_start=0xc000009c, __pa(__early_start)=0x10000009c
    

    修改方法很简单, 同时写入addr_h和addr_l, 从核就能成功启动.

    //增加写入addr_h
    out_be32(&spin_table->addr_h, __pa(__early_start) >> 32);
    out_be32(&spin_table->addr_l, __pa(__early_start));
    
  • 注for注1: 还有一个bug
    virt_to_phys(high_memory)=0x30000000这里是有问题的.
    我们的phy地址是4G以上的, 所以这里的0x30000000显然不对.
    问题出在arch/powerpc/include/asm/io.h
    virt_to_phys返回的是unsigned long, 在PPC32机器上, unsigned long是32位, 不够装下我们的phy地址.

    static inline unsigned long virt_to_phys(volatile void * address)
    {
      //__pa的返回类型是64位的, 但从本函数返回被截断了.
      return __pa((unsigned long)address);
    }
    

    历史背景: virt_to_phys被kernel里面很多核心代码和众多driver引用. unsigned long在32位机器上是32位, 在64位机器上是64位, 所以这个api被设计成默认虚拟地址和物理地址等宽. 一般情况下, 这样设计没问题.
    PPC4080, CPU是32位, 但物理地址有36位, 这种情况下, 如果DDR被配置在物理0地址, 大小不超过4G, 这样做也是没问题的.
    但fant很特殊, DDR被配置在了物理4G地址开始, 其物理地址"真的"超过了32位. 在这种情况下, virt_to_phys返回的物理地址会被截断.
    修改:
    也很简单, 把unsigned long换成phys_addr_t, phys_addr_t足够容下物理地址.
    此时打印如下:virt_to_phys(high_memory)地址变正确了.

    cpu_rel_addr=0xc3fdc4ac *cpu_rel_addr=0x11fee4180
    high_memory=0xf0000000 virt_to_phys(high_memory)=0x130000000
    __early_start=0xc000009c, __pa(__early_start)=0x10000009c
    
  • 注2:
    从核的spin table被主核更新为, 从核要跳转到物理地址00000001 0000009c执行.

    ppcP3041[2,s] % md 0x1fee4180
    0x1fee4180:
    1fee_4180   00000001 0000009c 00000000 00000002              ................
    1fee_4190   00000000 00000002 00000000 00000000              ................
    1fee_41a0   00000000 00000000 00000000 00000000              ................
    1fee_41b0   00000000 00000000 00000000 00000000              ................
    

    此时TLB为:

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xfffff000 --   0V--   1  0-I---------SrSwSx -> 0x0:fffff000 --------------Iprot
    1 0 0x1fee4000 --   0VTs   1  0--MG-------SrSwSx -> 0x1:1fee4000 --------------Iprot
    5 0 0xfe000000 --   0V--   1  0-I-G-------SrSw-- -> 0xf:fe000000 --------------Iprot
    
  • 注3:
    TLB更新为

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0x00000000 --   0V--   8  0-----------SrSwSx -> 0x1:00000000 --------------Iprot
    1 0 0x1fee4000 --   0VTs   1  0--MG-------SrSwSx -> 0x1:1fee4000 --------------Iprot
    5 0 0xfe000000 --   0V--   1  0-I-G-------SrSw-- -> 0xf:fe000000 --------------Iprot
    

    但注意此时虚拟地址还不是0xc0000000开始的, 而是从0开始的.

  • 注4:
    此时就只剩linux配的一条有效tlb

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xc0000000 --   0V--   8  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    
  • 注5:
    从核不用计算low_mem(768M), 而是直接读TLBCAM的配置, 从而配置成和主核一样的3个常驻TLB, 给linux 的low_mem使用.
    与之对应的是high_mem(256M), 在high_mem的内存需要动态TLB才能访问.
    这中间的操作由bl loadcam_entrybl restore_to_as0包围, 期间多配了一个带Ts标记的TLB.

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xc0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    1 0 0xd0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:10000000 --------------Iprot
    2 0 0xe0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:20000000 --------------Iprot
    63 0 0xc0000000 --   0VTs   8  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    
  • 注6:
    从核开始运行IDLE进程, tlb最终为只有768M固定映射.

    idx w vaddr      GsLpidVTsSizeTidWIMGEUrUwUxSrSwSx    paddr        VfX0X1U0U1U2U3Iprot
    0 0 0xc0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:00000000 --------------Iprot
    1 0 0xd0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:10000000 --------------Iprot
    2 0 0xe0000000 --   0V--   9  0--M--------SrSwSx -> 0x1:20000000 --------------Iprot
    

results matching ""

    No results matching ""