1. Introduction

In the Linux kernel source analysis of setup_arch (2) introduced in the current startup phase of memory allocation function memblock_alloc, this memory allocation function in this article will introduce paging_init used for page table and memory allocation, The general flow of paging_init function is shown in the following figure.

2. paging_init

2.1 build_mem_type_table

This function adjusts the properties defined in the statically defined mem_types array based on the specific CPU architecture.

2.2 prepare_page_table

The function is to clear the page directory entry, source code roughly as follows. If the kernel is running in XIP mode, the kernel part of the page directory is skipped. Then the user space of the page directory entry [addr,PAGE_OFFSET] has been cleared. Finally, the page directory entry corresponding to the [__phys_to_virt(end), VMALLOC_START] kernel space is zeroed out except for the first memory block.

/* arch/arm/mm/mmu.c */
static inline void prepare_page_table(void)
{
 unsigned long addr;
 phys_addr_t end;
  / * < - (1) -- - > * /
 for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
  pmd_clear(pmd_off_k(addr));

#ifdef CONFIG_XIP_KERNEL
 addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
  / * < - (2) - > * /
 for(; addr < PAGE_OFFSET; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr)); end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
 if (end >= lowmem_limit)
  end = lowmem_limit;
  / * < - (3) - > * /
 for (addr = __phys_to_virt(end);
      addr < VMALLOC_START; addr += PMD_SIZE)
  pmd_clear(pmd_off_k(addr));
}
Copy the code

2.3 map_lowmem

This function maps memory with physical memory addresses less than lowmem_limit to kernel space. The actual memory mapping is done in create_mapping.

/* arch/arm/mm/mmu.c */
static void __init map_lowmem(void)
{... for_each_memblock(memory, reg) { start = reg->base; end = start + reg->size;if (end > lowmem_limit)
   end = lowmem_limit;
  if (start >= end)
   break;

  map.pfn = __phys_to_pfn(start);
  map.virtual = __phys_to_virt(start);
  map.length = end - start;
  map.type = MT_MEMORY;

  create_mapping(&map.false); }}Copy the code

The create_mapping function is created using PGD, PUD, PMD, and PTE. ARM32 uses secondary page tables, PMD, PTE. At the same time, because the memory management is carried out by page, according to the paging mechanism of ARM hardware MMU, the PTE corresponding to one PMD cannot completely occupy the whole page. In order to avoid memory waste, the PTE corresponding to two PMDS will be placed in one page at the software level. Details you can refer to file the arch/arm/include/asm/pgtable – 2 level. The comments in the h section. Finally, the alloc_init_pte function is called to map the specified range of memory region, where early_pte_alloc function is also called to allocate memory memblock_alloc function introduced in the Linux kernel source analysis setup_arch (2). Finally, the PTE page is written to PMD to complete the mapping.

/* arch/arm/mm/mmu.c */
static void __init alloc_init_pte(...).
{
 pte_t *start_pte = early_pte_alloc(pmd);
 pte_t *pte = start_pte + pte_index(addr);

 do {
  set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
  pfn++;
 } while(pte++, addr += PAGE_SIZE, addr ! = end); early_pte_install(pmd, start_pte, type->prot_l1); }Copy the code

2.4 devicemaps_init

Call early_alloc to allocate a page, then call early_trap_init to copy the table to a new page, and finally call create_mapping to map the page to 0xffff0000. If mDESC ->map_io exists, The I/OS related to the device are also mapped.

2.5 kmap_init

This function is very simple and maps the 2MB interval [PKMAP_BASE, PAGE_OFFSET] into kernel space.

/* arch/arm/mm/mmu.c */
static void __init kmap_init(void)
{
#ifdef CONFIG_HIGHMEM
 pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE),
  PKMAP_BASE, _PAGE_KERNEL_TABLE);
#endif
}
Copy the code

3. Summary

Build_mem_type_table is responsible for adjusting mem_types according to different CPU architectures. Prepare_page_table is responsible for clearing the page directory items in the area to be initialized. Then map_LOWmem is used to establish the page table mapping of the low-end memory region, and finally devicemaps_init is called to establish the mapping to the direction table and device IO. At this point, paging_init is basically finished, except for the bootmem_init function, which will be analyzed in the next article.