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\n");  
                        }  
    #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\n");  
        // 標記內核代碼所用內存  
        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\n", 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'\n", this, name_);  
    }  
      
    status_t VmAspace::Init() {  
        DEBUG_ASSERT(magic_ == MAGIC);  
      
        LTRACEF("%p '%s'\n", 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\n", 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\n", 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)\n", 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\n", 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)\n", 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內核有關內存管理的初始化完成。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM