zoukankan      html  css  js  c++  java
  • Magenta源代码笔记(3) —— 内存管理【转】

    转自:http://blog.csdn.net/boymax2/article/details/52550197

    版权声明:本文为博主原创文章,未经博主允许不得转载。
    
    Magenta内核支持虚拟地址的配置,依赖于cpu内的mmu模块。
    
    下面会从以下几个方面对Magenta内核内存管理方面的代码进行分析:
    
    1、mmu初始化,也就是硬件mmu的初始化,以底层寄存器操作为主,汇编
    
    2、pmm初始化,也就是代码中物理内存结构的初始化
    
    3、vmm初始化,也就是代码中虚拟内存结构的初始化
    
    
    mmu初始化
    
    mmu初始化的代码由汇编完成,其中主要涉及了以下几个结构
    
    TLB:内存映射表,其定义位于c代码中
    
    kernel/arch/arm/arm/mmu.c
    
    [cpp] view plain copy
    
        uint32_t arm_kernel_translation_table[TT_ENTRY_COUNT] __ALIGNED(16384) __SECTION(".bss.prebss.translation_table");  
    
    以及初始化的内存映射关系,以qemu-virt平台
    
    kernel/platform/qemu-virt/platform.c
    
    [cpp] view plain copy
    
        struct mmu_initial_mapping mmu_initial_mappings[] = {  
            /* all of memory */  
            {  
                .phys = MEMORY_BASE_PHYS, // 内存物理基地址  
                .virt = KERNEL_BASE, // 内存虚拟基地址  
                .size = MEMORY_APERTURE_SIZE,// 虚拟内存大小  
                .flags = 0,  
                .name = "memory"  
            },  
          
            /* 1GB of peripherals */  
            {  
                .phys = PERIPHERAL_BASE_PHYS, // 外设物理基地址  
                .virt = PERIPHERAL_BASE_VIRT, // 外设虚拟基地址  
                .size = PERIPHERAL_BASE_SIZE, // 虚拟内存大小  
                .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,  
                .name = "peripherals"  
            },  
          
            /* null entry to terminate the list */  
            { 0 }  
        };  
    
    这两个结构都会在之后的汇编代码中使用。
    
    mmu初始化的汇编代码位于内核的启动文件中,以arm32为例
    
    自己对arm汇编不是很熟悉,在读汇编代码时花费了比较多的时间,希望有错误能指正出来
    
    启动文件中与mmu相关的代码已经提取出来
    
    在其中主要涉及到的操作为以下几个:
    
    1、重置mmu相关寄存器
    
    2、计算物理地址相对虚拟地址的偏移
    
    3、将tlb地址指向空间清零
    
    4、遍历mmu_initial_mappings结构,计算后写入tlb
    
    5、设置mmu相关寄存器
    
    6、跳转至c代码
    
    kernel/arch/arm/arm/start.S
    
    [plain] view plain copy
    
        #include <asm.h>  
        #include <arch/arm/cores.h>  
        #include <arch/arm/mmu.h>  
        #include <kernel/vm.h>  
          
        .section ".text.boot"  
        .globl _start  
        _start:  
            b   platform_reset  
            b   arm_undefined  
            b   arm_syscall  
            b   arm_prefetch_abort  
            b   arm_data_abort  
            b   arm_reserved  
            b   arm_irq  
            b   arm_fiq  
        #if WITH_SMP  
            b   arm_reset  
        #endif  
          
        .weak platform_reset  
        platform_reset:  
            /* Fall through for the weak symbol */  
          
        // arm复位处理程序  
        .globl arm_reset  
        arm_reset:  
            /* do some early cpu setup */  
            // 读SCTLR寄存器,手册P1711  
            mrc     p15, 0, r12, c1, c0, 0  
            /* i/d cache disable, mmu disabled */  
            // cache位与mmu位置0  
            bic     r12, #(1<<12)  
            bic     r12, #(1<<2 | 1<<0)  
        #if WITH_KERNEL_VM  
            /* enable caches so atomics and spinlocks work */  
            // cache位与mmu位置1  
            orr     r12, r12, #(1<<12)  
            orr     r12, r12, #(1<<2)  
        #endif // WITH_KERNEL_VM  
            // 写SCTLR寄存器  
            mcr     p15, 0, r12, c1, c0, 0  
          
            /* calculate the physical offset from our eventual virtual location */  
            // 计算物理地址相对虚拟地址的偏移,用于之后的转换  
        .Lphys_offset:  
            ldr     r4, =.Lphys_offset  
            adr     r11, .Lphys_offset  
            sub     r11, r11, r4  
          
        ...  
          
        #if ARM_WITH_MMU  
        .Lsetup_mmu:  
          
            /* set up the mmu according to mmu_initial_mappings */  
          
            /* load the base of the translation table and clear the table */  
            // 获取转换表地址  
            ldr     r4, =arm_kernel_translation_table  
            // 获取转换表物理地址  
            add     r4, r4, r11  
                /* r4 = physical address of translation table */  
          
            mov     r5, #0  
            mov     r6, #0  
          
            /* walk through all the entries in the translation table, setting them up */  
            // 遍历转换表结构清零  
        0:  
            str     r5, [r4, r6, lsl #2]  
            add     r6, #1  
            cmp     r6, #4096  
            bne     0b  
          
            /* load the address of the mmu_initial_mappings table and start processing */  
            // 获取初始映射地址  
            ldr     r5, =mmu_initial_mappings  
            // 获取初始映射物理地址  
            add     r5, r5, r11  
                /* r5 = physical address of mmu initial mapping table */  
          
        // 初始映射遍历绑定至转换表  
        // 转换表的绑定 转换表中元素的高12位为物理基地址下标,低20位为mmu相关flag  
        .Linitial_mapping_loop:  
            // 把结构体加载到各个通用寄存器中  
            ldmia   r5!, { r6-r10 }  
                /* r6 = phys, r7 = virt, r8 = size, r9 = flags, r10 = name */  
          
            /* round size up to 1MB alignment */  
            // 上调size对齐1MB  
            ubfx        r10, r6, #0, #20  
            add     r8, r8, r10  
            add     r8, r8, #(1 << 20)  
            sub     r8, r8, #1  
          
            /* mask all the addresses and sizes to 1MB boundaries */  
            // 物理地址 虚拟地址 大小 右移20位 取高12位  
            lsr     r6, #20  /* r6 = physical address / 1MB */  
            lsr     r7, #20  /* r7 = virtual address / 1MB */  
            lsr     r8, #20  /* r8 = size in 1MB chunks */  
          
            /* if size == 0, end of list */  
            // 循环边界判断  
            cmp     r8, #0  
            beq     .Linitial_mapping_done  
          
            /* set up the flags */  
            // 设置mmu相关flag,放置在r10  
            ldr     r10, =MMU_KERNEL_L1_PTE_FLAGS  
            teq     r9, #MMU_INITIAL_MAPPING_FLAG_UNCACHED  
            ldreq   r10, =MMU_INITIAL_MAP_STRONGLY_ORDERED  
            beq     0f  
            teq     r9, #MMU_INITIAL_MAPPING_FLAG_DEVICE  
            ldreq   r10, =MMU_INITIAL_MAP_DEVICE  
                /* r10 = mmu entry flags */  
          
        0:  
            // 计算translation_table元素的值  
            // r10:mmu相关flag r6:物理地址高12位  
            // r12 = r10 | (r6 << 20)  
            // 高20位为物理地址,低12位为mmu相关flag  
            orr     r12, r10, r6, lsl #20  
                /* r12 = phys addr | flags */  
          
            /* store into appropriate translation table entry */  
            // r4:转换表物理基地址 r7:虚拟地址对应的section  
            // r12 -> [r4 + r7 << 2]  
            str     r12, [r4, r7, lsl #2]  
          
            /* loop until we're done */  
            // 准备下一个转换表元素的填充  
            add     r6, #1  
            add     r7, #1  
            subs    r8, #1  
            bne     0b  
          
            b       .Linitial_mapping_loop  
          
        .Linitial_mapping_done:  
            ...  
          
            /* set up the mmu */  
            bl      .Lmmu_setup  
        #endif // WITH_KERNEL_VM  
          
            ...  
           // 跳转至c程序  
            bl      lk_main  
            b       .  
          
        #if WITH_KERNEL_VM  
            /* per cpu mmu setup, shared between primary and secondary cpus  
               args:  
               r4 == translation table physical  
               r8 == final translation table physical (if using trampoline)  
            */  
        // 设置mmu相关寄存器  
        // r4:转换表物理基地址  
        // mmu相关寄存器 手册P1724  
        .Lmmu_setup:  
            /* Invalidate TLB */  
            mov     r12, #0  
            mcr     p15, 0, r12, c8, c7, 0  
            isb  
          
            /* Write 0 to TTBCR */  
            // ttbcr写0  
            mcr     p15, 0, r12, c2, c0, 2  
            isb  
          
            /* Set cacheable attributes on translation walk */  
            // 宏MMU_TTBRx_FLAGS为 (1 << 3) | (1 << 6)  
            orr     r12, r4, #MMU_TTBRx_FLAGS  
          
            /* Write ttbr with phys addr of the translation table */  
            // 写入ttbr0  
            mcr     p15, 0, r12, c2, c0, 0  
            isb  
          
            /* Write DACR */  
            // 写DACR cache相关  
            mov     r12, #0x1  
            mcr     p15, 0, r12, c3, c0, 0  
            isb  
          
            /* Read SCTLR into r12 */  
            // 读SCTLR寄存器,手册P1711  
            mrc     p15, 0, r12, c1, c0, 0  
          
            /* Disable TRE/AFE */  
            // 禁用TRE和AFE标志位  
            bic     r12, #(1<<29 | 1<<28)  
          
            /* Turn on the MMU */  
            // MMU使能标志位  
            orr     r12, #0x1  
          
            /* Write back SCTLR */  
            // 写入SCTLR  
            // MMU打开  
            mcr     p15, 0, r12, c1, c0, 0  
            isb  
          
            /* Jump to virtual code address */  
            // 跳转  
            ldr     pc, =1f  
        1:  
            ...  
          
            /* Invalidate TLB */  
            mov     r12, #0  
            mcr     p15, 0, r12, c8, c7, 0  
            isb  
          
            /* assume lr was in physical memory, adjust it before returning */  
            // 计算跳转点的虚拟地址,跳转,之后会调用lk_main  
            sub     lr, r11  
            bx      lr  
        #endif  
          
        ...  
    
    硬件层的内存管理相关的初始化基本完成后,会跳转到c代码
    
    位于kernel/top/main.c
    
    其中有关内存管理的函数调用顺序为:
    
    1、pmm_add_arena 将物理内存加入pmm结构
    
    2、vm_init_preheap 堆初始化前的准备工作(钩子)
    
    3、heap_init 堆的初始化
    
    4、vm_init_postheap 堆初始化后的工作(钩子)
    
    5、arm_mmu_init mmu相关的调整
    
    
    首先要完成pmm初始化工作
    
    pmm初始化主要分为以下几步:
    
    1、通过fdt库从bootloader中获取物理内存的长度
    
    2、在pmm中加入物理内存
    
    3、标记fdt结构的空间
    
    4、标记bootloader相关的空间
    
    pmm中比较重要的一个结构体,pmm_arena_t代表着一块物理内存的抽象
    
    kernel/include/kernel/vm.h
    
    [cpp] view plain copy
    
        typedef struct pmm_arena {  
            struct list_node node; // 节点,物理内存链表  
            const char* name; // 名称  
          
            uint flags;  
            uint priority;  
          
            paddr_t base; // 物理内存基地址  
            size_t size; // 物理内存长度  
          
            size_t free_count; // 空闲的页数  
          
            struct vm_page* page_array; // 页结构数组  
            struct list_node free_list; // 节点,该内存中空闲空间的链表  
        } pmm_arena_t;  
    
    接着以qemu-virt的platform为例,分析pmm初始化的过程
    
    kernel/platform/qemu-virt.c
    
    [cpp] view plain copy
    
        // 全局物理内存结构体  
        static pmm_arena_t arena = {  
            .name = "ram",  
            .base = MEMORY_BASE_PHYS,  
            .size = DEFAULT_MEMORY_SIZE,  
            .flags = PMM_ARENA_FLAG_KMAP,  
        };  
        ...  
        // 该函数为平台的早期初始化,在内核启动时调用  
        void platform_early_init(void)  
        {  
            ...  
            /* look for a flattened device tree just before the kernel */  
            // 获取fdt结构  
            const void *fdt = (void *)KERNEL_BASE;  
            int err = fdt_check_header(fdt);  
            if (err >= 0) {  
                /* walk the nodes, looking for 'memory' and 'chosen' */  
                int depth = 0;  
                int offset = 0;  
                for (;;) {  
                    offset = fdt_next_node(fdt, offset, &depth);  
                    if (offset < 0)  
                        break;  
          
                    /* get the name */  
                    const char *name = fdt_get_name(fdt, offset, NULL);  
                    if (!name)  
                        continue;  
          
                    /* look for the properties we care about */  
                    // 从fdt中查找到内存信息  
                    if (strcmp(name, "memory") == 0) {  
                        int lenp;  
                        const void *prop_ptr = fdt_getprop(fdt, offset, "reg", &lenp);  
                        if (prop_ptr && lenp == 0x10) {  
                            /* we're looking at a memory descriptor */  
                            //uint64_t base = fdt64_to_cpu(*(uint64_t *)prop_ptr);  
                            // 获取内存长度  
                            uint64_t len = fdt64_to_cpu(*((const uint64_t *)prop_ptr + 1));  
          
                            /* trim size on certain platforms */  
        #if ARCH_ARM  
                            // 如果是32位arm,只使用内存前1GB  
                            if (len > 1024*1024*1024U) {  
                                len = 1024*1024*1024; /* only use the first 1GB on ARM32 */  
                                printf("trimming memory to 1GB
    ");  
                            }  
        #endif  
                            /* set the size in the pmm arena */  
                            // 保存内存长度  
                            arena.size = len;  
                        }  
                    } else if (strcmp(name, "chosen") == 0) {  
                        ...  
                    }  
                }  
            }  
          
            /* add the main memory arena */  
            // 将改内存区域加入到pmm中  
            pmm_add_arena(&arena);  
          
            /* reserve the first 64k of ram, which should be holding the fdt */  
            // 标记fdt区域  
            pmm_alloc_range(MEMBASE, 0x10000 / PAGE_SIZE, NULL);  
          
            // 标记bootloader_ramdisk区域  
            platform_preserve_ramdisk();  
            ...  
        }  
    
    内核在接下来初始化堆之前会在内存中构造出出一个VmAspace对象,其代表的是内核空间的抽象
    
    kernel/kernel/vm/vm.cpp
    
    [cpp] view plain copy
    
        void vm_init_preheap(uint level) {  
            LTRACE_ENTRY;  
          
            // allow the vmm a shot at initializing some of its data structures  
            // 构造代表内核空间的VmAspace对象  
            VmAspace::KernelAspaceInitPreHeap();  
          
            // mark all of the kernel pages in use  
            LTRACEF("marking all kernel pages as used
    ");  
            // 标记内核代码所用内存  
            mark_pages_in_use((vaddr_t)&_start, ((uintptr_t)&_end - (uintptr_t)&_start));  
          
            // mark the physical pages used by the boot time allocator  
            // 标记boot time allocator代码所用内存  
            if (boot_alloc_end != boot_alloc_start) {  
                LTRACEF("marking boot alloc used from 0x%lx to 0x%lx
    ", boot_alloc_start, boot_alloc_end);  
          
                mark_pages_in_use(boot_alloc_start, boot_alloc_end - boot_alloc_start);  
            }  
        }  
    
    kernel/kernel/vm/vm_aspace.cpp
    
    [cpp] view plain copy
    
        void VmAspace::KernelAspaceInitPreHeap() {  
            // the singleton kernel address space  
            // 构造一个内核空间单例,因为这个函数只会在启动时调用,所以是这个对象是单例  
            static VmAspace _kernel_aspace(KERNEL_ASPACE_BASE, KERNEL_ASPACE_SIZE, VmAspace::TYPE_KERNEL,  
                                           "kernel");  
            // 初始化  
            auto err = _kernel_aspace.Init();  
            ASSERT(err >= 0);  
          
            // save a pointer to the singleton kernel address space  
            // 保存单例指针  
            VmAspace::kernel_aspace_ = &_kernel_aspace;  
        }  
          
        VmAspace::VmAspace(vaddr_t base, size_t size, uint32_t flags, const char* name)  
            : base_(base), size_(size), flags_(flags) {  
          
            DEBUG_ASSERT(size != 0);  
            DEBUG_ASSERT(base + size - 1 >= base);  
          
            Rename(name);  
          
            LTRACEF("%p '%s'
    ", this, name_);  
        }  
          
        status_t VmAspace::Init() {  
            DEBUG_ASSERT(magic_ == MAGIC);  
          
            LTRACEF("%p '%s'
    ", this, name_);  
          
            // intialize the architectually specific part  
            // 标记为内核的空间  
            bool is_high_kernel = (flags_ & TYPE_MASK) == TYPE_KERNEL;  
            uint arch_aspace_flags = is_high_kernel ? ARCH_ASPACE_FLAG_KERNEL : 0;  
            // 调用mmu相关的函数  
            return arch_mmu_init_aspace(&arch_aspace_, base_, size_, arch_aspace_flags);  
        }  
    
    kernel/arch/arm/arm/mmu.c
    
    [cpp] view plain copy
    
        status_t arch_mmu_init_aspace(arch_aspace_t *aspace, vaddr_t base, size_t size, uint flags)  
        {  
            LTRACEF("aspace %p, base 0x%lx, size 0x%zx, flags 0x%x
    ", aspace, base, size, flags);  
          
            DEBUG_ASSERT(aspace);  
            DEBUG_ASSERT(aspace->magic != ARCH_ASPACE_MAGIC);  
          
            /* validate that the base + size is sane and doesn't wrap */  
            DEBUG_ASSERT(size > PAGE_SIZE);  
            DEBUG_ASSERT(base + size - 1 > base);  
          
            // 初始化内核空间中页的链表  
            list_initialize(&aspace->pt_page_list);  
          
            aspace->magic = ARCH_ASPACE_MAGIC;  
            if (flags & ARCH_ASPACE_FLAG_KERNEL) {  
                // 设置结构内相关参数,其中转换表的物理内存通过vaddr_to_paddr获取  
                // 该函数不详细分析了,实质就是通过转换表进行查询得到的物理地址  
                aspace->base = base;  
                aspace->size = size;  
                aspace->tt_virt = arm_kernel_translation_table;  
                aspace->tt_phys = vaddr_to_paddr(aspace->tt_virt);  
            } else {  
                ...  
            }  
          
            LTRACEF("tt_phys 0x%lx tt_virt %p
    ", aspace->tt_phys, aspace->tt_virt);  
          
            return NO_ERROR;  
        }  
    
    到此内核空间的结构初始化完成
    
    接下来进行内核堆的初始化,Magenta内核中提供了两种堆的实现miniheap以及cmpctmalloc,用户可以自己进行配置。
    
    堆的具体实现方法会在之后进行具体的分析
    
    堆的初始化完成以后,会调用相应的钩子函数,该函数的主要的作用如下:
    
    1、在vmm结构中标记内核已使用的虚拟地址
    
    2、根据内核使用的地址的区域,分别设置内存的保护
    
    [cpp] view plain copy
    
        void vm_init_postheap(uint level) {  
            LTRACE_ENTRY;  
          
            vmm_aspace_t* aspace = vmm_get_kernel_aspace();  
          
            // we expect the kernel to be in a temporary mapping, define permanent  
            // regions for those now  
            struct temp_region {  
                const char* name;  
                vaddr_t base;  
                size_t size;  
                uint arch_mmu_flags;  
            } regions[] = {  
                {  
                    .name = "kernel_code",  
                    .base = (vaddr_t)&__code_start,  
                    .size = ROUNDUP((size_t)&__code_end - (size_t)&__code_start, PAGE_SIZE),  
                    .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE,  
                },  
                {  
                    .name = "kernel_rodata",  
                    .base = (vaddr_t)&__rodata_start,  
                    .size = ROUNDUP((size_t)&__rodata_end - (size_t)&__rodata_start, PAGE_SIZE),  
                    .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ,  
                },  
                {  
                    .name = "kernel_data",  
                    .base = (vaddr_t)&__data_start,  
                    .size = ROUNDUP((size_t)&__data_end - (size_t)&__data_start, PAGE_SIZE),  
                    .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,  
                },  
                {  
                    .name = "kernel_bss",  
                    .base = (vaddr_t)&__bss_start,  
                    .size = ROUNDUP((size_t)&__bss_end - (size_t)&__bss_start, PAGE_SIZE),  
                    .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,  
                },  
                {  
                    .name = "kernel_bootalloc",  
                    .base = (vaddr_t)boot_alloc_start,  
                    .size = ROUNDUP(boot_alloc_end - boot_alloc_start, PAGE_SIZE),  
                    .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,  
                },  
            };  
          
            for (uint i = 0; i < countof(regions); ++i) {  
                temp_region* region = &regions[i];  
                ASSERT(IS_PAGE_ALIGNED(region->base));  
                status_t status = vmm_reserve_space(aspace, region->name, region->size, region->base);  
                ASSERT(status == NO_ERROR);  
                status = vmm_protect_region(aspace, region->base, region->arch_mmu_flags);  
                ASSERT(status == NO_ERROR);  
            }  
          
            // mmu_initial_mappings should reflect where we are now, use it to construct the actual  
            // mappings.  We will carve out the kernel code/data from any mappings and  
            // unmap any temporary ones.  
            const struct mmu_initial_mapping* map = mmu_initial_mappings;  
            for (map = mmu_initial_mappings; map->size > 0; ++map) {  
                LTRACEF("looking at mapping %p (%s)
    ", map, map->name);  
                // Unmap temporary mappings except where they intersect with the  
                // kernel code/data regions.  
                vaddr_t vaddr = map->virt;  
                LTRACEF("vaddr 0x%lx, virt + size 0x%lx
    ", vaddr, map->virt + map->size);  
                while (vaddr != map->virt + map->size) {  
                    vaddr_t next_kernel_region = map->virt + map->size;  
                    vaddr_t next_kernel_region_end = map->virt + map->size;  
          
                    // Find the kernel code/data region with the lowest start address  
                    // that is within this mapping.  
                    for (uint i = 0; i < countof(regions); ++i) {  
                        temp_region* region = &regions[i];  
          
                        if (region->base >= vaddr && region->base < map->virt + map->size &&  
                            region->base < next_kernel_region) {  
          
                            next_kernel_region = region->base;  
                            next_kernel_region_end = region->base + region->size;  
                        }  
                    }  
          
                    // If vaddr isn't the start of a kernel code/data region, then we should make  
                    // a mapping between it and the next closest one.  
                    if (next_kernel_region != vaddr) {  
                        status_t status =  
                            vmm_reserve_space(aspace, map->name, next_kernel_region - vaddr, vaddr);  
                        ASSERT(status == NO_ERROR);  
          
                        if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {  
                            // If the region is part of a temporary mapping, immediately unmap it  
                            LTRACEF("Freeing region [%016lx, %016lx)
    ", vaddr, next_kernel_region);  
                            status = vmm_free_region(aspace, vaddr);  
                            ASSERT(status == NO_ERROR);  
                        } else {  
                            // Otherwise, mark it no-exec since it's not explicitly code  
                            status = vmm_protect_region(  
                                aspace,  
                                vaddr,  
                                ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);  
                            ASSERT(status == NO_ERROR);  
                        }  
                    }  
                    vaddr = next_kernel_region_end;  
                }  
            }  
        }  
    
    以上代码中涉及到的几个函数,只是做下简单的介绍,不具体分析:
    
    vmm_reserve_space:在vmm中标记一块虚拟内存,这块虚拟内存抽象为VmRegion类,拥有自己的底层mmu相关的配置
    
    vmm_protect_region:对某VmRegion对应的虚拟内存设置内存保护的相关参数
    
    
    mmu相关的调整
    mmu相关的调整,由内核新建的bootstrap2线程进行调用arch_init完成
    
    kernel/arch/arm/arm/arch.c
    
    [cpp] view plain copy
    
        void arch_init(void)  
        {  
           ...  
        #if ARM_WITH_MMU  
            /* finish intializing the mmu */  
            arm_mmu_init();  
        #endif  
        }  
    
    kernel/arch/arm/arm/mmu.c
    
    [cpp] view plain copy
    
        void arm_mmu_init(void)  
        {  
            /* unmap the initial mapings that are marked temporary */  
            // 解除具有MMU_INITIAL_MAPPING_TEMPORARY标志的内存映射  
            struct mmu_initial_mapping *map = mmu_initial_mappings;  
            while (map->size > 0) {  
                if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {  
                    vaddr_t va = map->virt;  
                    size_t size = map->size;  
          
                    DEBUG_ASSERT(IS_SECTION_ALIGNED(size));  
          
                    while (size > 0) {  
                        arm_mmu_unmap_l1_entry(arm_kernel_translation_table, va / SECTION_SIZE);  
                        va += MB;  
                        size -= MB;  
                    }  
                }  
                map++;  
            }  
            arm_after_invalidate_tlb_barrier();  
          
        #if KERNEL_ASPACE_BASE != 0  
            /* bounce the ttbr over to ttbr1 and leave 0 unmapped */  
            // 重新设置mmu相关的寄存器,禁用ttbcr0,将原先ttbr0的映射移动到ttbr1  
            // ttbr1为内核空间使用的寄存器  
            uint32_t n = __builtin_clz(KERNEL_ASPACE_BASE) + 1;  
            DEBUG_ASSERT(n <= 7);  
          
            uint32_t ttbcr = (1<<4) | n; /* disable TTBCR0 and set the split between TTBR0 and TTBR1 */  
          
            arm_write_ttbr1(arm_read_ttbr0());  
            ISB;  
            arm_write_ttbcr(ttbcr);  
            ISB;  
            arm_write_ttbr0(0);  
            ISB;  
        #endif  
        }  
    
    
    至此Magenta内核有关内存管理的初始化完成。
  • 相关阅读:
    C++ 重载操作符- 01 简单的入门
    C++ 析构函数
    Auto Control 001 自动控制的一般概念
    C++ 友元
    安装 SQL Server 2014 Express
    关闭是否只查看安全传送的网页内容提示框 和 是否允许运行软件,如ActiveX控件和插件提示框
    Python 网络爬虫 010 (高级功能) 解析 robots.txt 文件
    2019-06-12 Java学习日记之JDBC
    2019-06-11 Java学习日记之Bootstrap
    2019-06-10 Java学习日记之JQuery
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/6871930.html
Copyright © 2011-2022 走看看