Linux内核物理内存映射

Linux内核物理内存映射,在内核使用内存前,需要初始化内核的页表,初始化页表主要在map_lowmem()函数中。在映射页表之前,需要把页表的页表项清零,主要在prepare_page_table()函数中实现。

      [start_kernel()->setup_arch()->paging_init()]

      static inline void prepare_page_table(void)
      {
            unsigned long addr;
            phys_addr_t end;

            /*
            * Clear out all the mappings below the kernel image.
            */
            for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
                pmd_clear(pmd_off_k(addr));

            for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
                pmd_clear(pmd_off_k(addr));
            /*

            * Find the end of the first block of lowmem.
            */
            end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
            /*
            * Clear out all the kernel space mappings, except for the first
            * memory bank, up to the vmalloc region.
            */
            for (addr = __phys_to_virt(end);
                  addr < VMALLOC_START; addr += PMD_SIZE)
                  pmd_clear(pmd_off_k(addr));
        }

这里对如下3段地址调用pmd_clear()函数来清除一级页表项的内容。

  • 0x0~MODULES_VADDR。
  • MODULES_VADDR~PAGE_OFFSET。
  • arm_lowmem_limit~VMALLOC_START。
      [start_kernel()->setup_arch()->paging_init()->map_lowmem()]

      static void    init map lowmem(void)
      {
            struct memblock region *reg;
            phys addr t kernel x start = round down(  pa( stext), SECTION SIZE);
            phys addr t kernel x end = round up(  pa(  init end), SECTION SIZE);

            /* Map all the lowmem memory banks. */
            for each memblock(memory, reg) {
                phys addr t start = reg->base;
                phys addr t end = start + reg->size;
                struct map desc map;

                if (end > arm lowmem limit)
                      end = arm lowmem limit;

                //映射kernel image区域
                map.pfn =    phys to pfn(kernel x start);
                map.virtual =    phys to virt(kernel x start);
                map.length = kernel x end - kernel x start;
                map.type = MT MEMORY RWX;
                create_mapping(&map);
                //映射低端内存
                if (kernel_x_end < end) {
                    map.pfn = __phys_to_pfn(kernel_x_end);
                    map.virtual = __phys_to_virt(kernel_x_end);
                    map.length = end - kernel_x_end;
                    map.type = MT_MEMORY_RW;
                    create_mapping(&map);

                }
            }
      }

真正创建页表是在map_lowmem()函数中,会从内存开始的地方覆盖到arm_lowmem_limit处。这里需要考虑kernel代码段的问题,kernel的代码段从_stext开始,到_init_end结束。以ARM Vexpress平台为例。

  • 内存开始地址0x60000000。
  • _stext:0x60000000。
  • _init_end: 0x60800000。
  • arm_lowmem_limit: 0x8f800000。

其中,arm_lowmem_limit地址需要考虑高端内存的情况,该值的计算是在sanity_check_meminfo()函数中。在ARM Vexpress平台中,arm_lowmem_limit等于vmalloc_min,其定义如下:

      static void * __initdata vmalloc_min =
            (void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);

      phys addr t vmalloc limit =    pa(vmalloc min - 1) + 1;

map_lowmem()会对两个内存区间创建映射。

(1)区间1

  • 物理地址:0x60000000~0x608000000。
  • 虚拟地址:0xc0000000~0xc08000000。
  • 属性:可读、可写并且可执行(MT_MEMORY_RWX)。

(2)区间2

  • 物理地址:0x60800000~0x8f8000000。
  • 虚拟地址:0xc0800000~0xef8000000。
  • 属性:可读、可写(MT_MEMORY_RW)。

MT_MEMORY_RWX和MT_MEMORY_RW的区别在于\2RM页表项有一个XN比特位,XN比特位置为1,表示这段内存区域不允许执行。

映射函数为create_mapping(),这里创建的映射就是物理内存直接映射,或者叫作线性映射。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!