• U-Boot内存管理


    如《Linux内核内存管理架构》一文中提到,linux内核中的内存管理支持内存地址映射、内存分配、内存回收、内存碎片管理、页面缓存等众多功能。但U-Boot做为启动引导程序,其核心功能就是引导内核镜像,所以其内存管理功能并不用像Linux内核中的内存管理一样功能齐全。U-Boot中没有内存分配、回收、缓存等功能,内存管理其实只做一件事:虚实地址映射,而且是固定映射。

    为了提高效率,现代处理器的内存管理都由MMU(Memory Management Unit)硬件单元实现(示意图如下),其核心模块主要有TLB和page table。

    不同的CPU体系的MMU架构差异很大,有的MMU可以跳过,有的不可以;有的MMU分为L1MMU、L2MMU;有的TLB分为页式TLB、段式TLB;有的page table分为page entry、block entry;有的TLB reload和page table 查找由MMU硬件实现(ARM、x86, PowerPC),有的则是由软件实现(MIPS, Alpha)等。U-Boot的虚实地址映射,一般能跳过MMU就跳过,能不使用页表就不用页表,总之,怎么简单怎么来。

    可见,U-Boot中的内存管理的实现与CPU架构和MMU强相关,本文挑选了PowerPC e500,MIPS,ARMv8三款处理器并对其MMU架构进行分析,并讨论它们在U-Boot中的内存虚实地址映射实现。

    U-Boot PowerPC内存管理

    下图是PowerPC e500的MMU结构框图。

    MMU地址映射过程中涉及到3种地址形式:

    1. 32位有效地址EA:软件可以直接访问的地址;
    2. 41位虚拟地址VA:经过段映射的过渡地址,由32位EA和AS以及8位PID位组成;
    3. 36位物理地址RA:也称为实地址,由36位地址总线访问的地址空间。

    PID0-2 用来存放当前进程有效地址的进程ID号,主要作用是在进程上下文切换时,提高TLB刷新的精准性。

    LAW(Local Access Window) 用于描述PowerPC处理器物理地址空间的划分,其中LAWBAR用于指定基址,LAWAR用于指定此空间用作PCI、Local Bus还是DDR等设备的空间。

    e500支持2种形式的TLB:TLB0和TLB1。

    TLB0支持固定4K页大小映射,512个entry最大可以映射512*4K=2M的物理地址空间,需要动态更新,会产生TLB miss异常。TLB0灵活,可以满足复杂系统应用的要求。
    TLB1 是一种段式映射,有效地址和物理地址之间是一一对应的关系。TLB1支持可变页大小映射,16个entry 支持4K~4G页大小的映射,最大可映射16*4G =64G的物理地址空间,不需要动态更新,不会产生TLB miss异常。TLB1不够灵活,无法满足复杂系统应用的要求。

    PowerPC e500核心的MMU是无法跳过的,所以只能通过MMU来映射地址空间。虽然MMU中的TLB1段式映射不够灵活,但是简单,可以满足U-Boot中的内存固定映射需求。PowerPC对内存虚实地址映射的处理是先设置DDR内存的物理地址(LAW),再把虚拟地址到物理地址的映射关系写到TLB1中。

    相关代码实现:

    phys_size_t fixed_sdram(void)
    {
    // ... 初始化DDR配置参数ddr_cfg_regs
        ddr_size = (phys_size_t) CONFIG_SYS_SDRAM_SIZE * 1024 * 1024;
        fsl_ddr_set_memctl_regs(&ddr_cfg_regs, 0);
    
        if (set_ddr_laws(CONFIG_SYS_DDR_SDRAM_BASE, ddr_size,
                        LAW_TRGT_IF_DDR_1) < 0) {
            printf("ERROR setting Local Access Windows for DDR
    ");
            return 0;
        }
    
        return ddr_size;
    }
    
    unsigned int
    setup_ddr_tlbs_phys(phys_addr_t p_addr, unsigned int memsize_in_meg)
    {
     /// ... 计算size
    
        for (i = 0; size && i < 8; i++) {
    /// ... 计算ram_tlb_address, p_addr,  ram_tlb_index, tlb_size
    
            set_tlb(1, ram_tlb_address, p_addr,
                MAS3_SX|MAS3_SW|MAS3_SR, wimge,
                0, ram_tlb_index, tlb_size, 1);
        }
    
        return memsize_in_meg;
    }
    
    unsigned int setup_ddr_tlbs(unsigned int memsize_in_meg)
    {
        return
            setup_ddr_tlbs_phys(CONFIG_SYS_DDR_SDRAM_BASE, memsize_in_meg);
    }

    U-Boot MIPS内存管理

    MIPS的虚拟地址空间分为多个段,即Kseg0-3和Kuseg。其中kseg0/kseg1可以跳过MMU,支持直接映射,各512MB。kseg2/3和Kuseg必须经过MMU。

    因为U-Boot阶段对内存的需求量很小,512MB内存空间已足够满足需要,所以MIPS对内存虚实地址映射的处理是先设置好DDR内存的物理地址BAR,把其虚拟地址设置到kseg0/1即可。没有必要经过MMU和TLB。换句话说,如果CPU需要访问高于512M的DDR内存物理地址空间,必须通过MMU地址转换。

     

    相关代码实现:

    #define mem_map(x) (void *)(CAC_BASE + (x))

    U-Boot ARMv8内存管理

     ARMv8的MMU结构如下图,其支持:

    • L1指令TLB,全相连,48个entry,支持4KB, 64KB和1MB页面大小;
    • L1数据TLB,全相连,32个entry,支持4KB, 64KB和1MB页面大小;
    • L2 TLB,4路组相连,1024个entry,支持4KB, 64KB和1MB页面大小;
    • page table 查找由MMU中的Translation Control Unit (TCU) 硬件实现;

    ARMv8的page table 结构如下。其分为4级,每级页表有512个条目,支持如下3种表条目描述符。

    • Table descriptor,指向下一级table;
    • Page descriptor,指向一个4KB(或64KB、1MB)大小page size的页面;
    • Block descriptor,指向一个block size的内存区域;在1级页表中block size是1GB,2级页表中block size是2MB。

     

    与上面提到的PowerPC、MIPS不同,ARMv8的MMU即无法跳过,TLB也不支持段式映射。所以只能通过page table完成虚实地址映射。这就带来一个问题:在DDR内存准备好之前,page table又放在哪里?

    ARMv8核心的处理器片内包含一块2MB大小的OCRAM(On Chip RAM),在DDR RAM准备好之前,MMU table就放在CONFIG_SYS_FSL_OCRAM_BASE处,最大支持EARLY_PGTABLE_SIZE(0x5000)个条目。2MB大小的页表在Linux内核中是远远不够的,但在U--Boot中已足够,因为:

    1. page table支持块映射(1GB,2MB),对于连续的大块地址空间映射,block entry能极大简化page table的大小;
    2. U-Boot中的虚实地址映射都是固定映射;
    3. 只有和启动相关的设备和地址空间才需要映射,其数量有限。

    等DDR内存初始化好后,再将table放到DRAM中。

    相关代码实现:

    void setup_pgtables(void)
    {
        int i;
    
        if (!gd->arch.tlb_fillptr || !gd->arch.tlb_addr)
            panic("Page table pointer not setup.");
    
        create_table();
    
        /* Now add all MMU table entries one after another to the table */
        for (i = 0; mem_map[i].size || mem_map[i].attrs; i++)
            add_map(&mem_map[i]);
    }
    
    static void add_map(struct mm_region *map)
    {
        u64 *pte;
        u64 virt = map->virt;
        u64 phys = map->phys;
        u64 size = map->size;
        u64 attrs = map->attrs | PTE_TYPE_BLOCK | PTE_BLOCK_AF;
        u64 blocksize;
        int level;
        u64 *new_table;
    
        while (size) {
            pte = find_pte(virt, 0);
            if (pte && (pte_type(pte) == PTE_TYPE_FAULT)) {
                debug("Creating table for virt 0x%llx
    ", virt);
                new_table = create_table();
                set_pte_table(pte, new_table);
            }
    
            for (level = 1; level < 4; level++) {
                pte = find_pte(virt, level);
                if (!pte)
                    panic("pte not found
    ");
    
                blocksize = 1ULL << level2shift(level);
                if (size >= blocksize && !(virt & (blocksize - 1))) {
                    /* Page fits, create block PTE */
                    *pte = phys | attrs;
                    virt += blocksize;
                    phys += blocksize;
                    size -= blocksize;
                    break;
                } else if (pte_type(pte) == PTE_TYPE_FAULT) {
                    /* Page doesn't fit, create subpages */
                    new_table = create_table();
                    set_pte_table(pte, new_table);
                } else if (pte_type(pte) == PTE_TYPE_BLOCK) {
                    split_block(pte, level);
                }
            }
        }
    }
  • 相关阅读:
    VS中注释的使用
    VS2010中:error C2471: 无法更新程序数据库
    VS2010 MFC中在对话框上添加工具栏以及工具栏提示信息并改变图标支持256色
    CToolBar与CToolBarCtrl以及CStatusBar 与CStatusBarCtrl的区别
    error C2664: “wcscpy”: 不能将参数 1 从“LPSTR”转换为“wchar_t *”
    VS2010 MFC中的Picture控件显示图像
    VS2010 MFC中屏蔽ESC和ENTER键关闭对话框的方法
    MFC中CImageList(图形列表控件)、CTreeCtrl(树形列表控件)的简单用法
    窗口类、窗口类对象与窗口 三者之间关系——孙鑫<VC++深入详解>
    VS2010 MFC中改变static字体颜色、大小、背景颜色(自定义类),及手动关联变量的方法
  • 原文地址:https://www.cnblogs.com/wahaha02/p/9712342.html
Copyright © 2020-2023  润新知