arch_mem_init()在调用bootmem_init()函数完成bootmem的初始化之后调用paging_init()进行分页系统的初始化。
分页系统就是将物理内存按页为单位进行描述,管理和映射。
注意这里是mips cpu,不同的arch cpu初始化顺序可能不同, linux-3.3.8
[arch/mips/mm/init.c]
void __init paging_init(void)
{
unsigned long max_zone_pfns[MAX_NR_ZONES];
unsigned long lastpfn __maybe_unused;
pagetable_init();
//页表初始化
#ifdef CONFIG_HIGHMEM
kmap_init();
#endif
kmap_coherent_init();
#ifdef CONFIG_ZONE_DMA
max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN;
#endif
#ifdef CONFIG_ZONE_DMA32
max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN;
#endif
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
lastpfn = max_low_pfn;
#ifdef CONFIG_HIGHMEM
max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;
lastpfn = highend_pfn;
if (cpu_has_dc_aliases && max_low_pfn != highend_pfn) {
printk(KERN_WARNING "This processor doesn't support highmem."
" %ldk highmem ignored\n",
(highend_pfn - max_low_pfn) << (PAGE_SHIFT - 10));
max_zone_pfns[ZONE_HIGHMEM] = max_low_pfn;
lastpfn = max_low_pfn;
}
#endif
free_area_init_nodes(max_zone_pfns);
}
初始化页表
[arch/mips/mm/pgtable-32.c]
void __init pagetable_init(void)
{
unsigned long vaddr;
pgd_t *pgd_base;
#ifdef CONFIG_HIGHMEM
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
#endif
/* Initialize the entire pgd. */
pgd_init((unsigned long)swapper_pg_dir);
// pgd_t swapper_pg_dir[_PTRS_PER_PGD] __page_aligned(_PGD_ORDER);
//
#define _PTRS_PER_PGD 1024 /* PTRS_PER_PGD # */ 2^10=1024个pgdir项
// typedef struct { unsigned long pgd; } pgd_t; 每个pgdir项占用4byte
//初始化用户态地址空间
0-0x7FFFFFFFUL
对应的pgdir项
pgd_init((unsigned long)swapper_pg_dir
//初始化内核态地址空间0x80000000UL-0xFFFFFFFF对应的pgdir项
+ sizeof(pgd_t) * USER_PTRS_PER_PGD);
pgd_base = swapper_pg_dir;
/*
* Fixed mappings:
//建立固定映射占用的虚拟地址空间对应的页表项
*/
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
fixrange_init(vaddr, vaddr + FIXADDR_SIZE, pgd_base);
#ifdef CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
//建立永久映射占用的虚拟地址空间对应的页表项
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);
pgd = swapper_pg_dir + __pgd_offset(vaddr);
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pkmap_page_table = pte;
#endif
}
#ifdef CONFIG_PAGE_SIZE_4KB
#define
PAGE_SHIFT 12
#endif
#define
PGDIR_SHIFT (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2)
//2*12-0-2=22,也就是说page gobal directory占用32-22=10bit
#define
PGDIR_SIZE (1UL << PGDIR_SHIFT)
//所以一个pgdir项对应的内存大小为1<<22
#define
USER_PTRS_PER_PGD (0x80000000UL/
PGDIR_SIZE)
//整个用户地址空间0-0x7FFFFFFFUL对应的pgd
ir项个
数
void pgd_init(unsigned long page)
{
unsigned long *p = (unsigned long *) page;
int i;
for (i = 0; i < USER_PTRS_PER_PGD; i+=8) {
p[i + 0] = (unsigned long) invalid_pte_table;
p[i + 1] = (unsigned long) invalid_pte_table;
p[i + 2] = (unsigned long) invalid_pte_table;
p[i + 3] = (unsigned long) invalid_pte_table;
p[i + 4] = (unsigned long) invalid_pte_table;
p[i + 5] = (unsigned long) invalid_pte_table;
p[i + 6] = (unsigned long) invalid_pte_table;
p[i + 7] = (unsigned long) invalid_pte_table;
}
}
void __init fixrange_init(unsigned long start, unsigned long end,
pgd_t *pgd_base)
{
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_MIPS_MT_SMTC)
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
int i, j, k;
unsigned long vaddr;
vaddr = start;
i = __pgd_offset(vaddr);
j = __pud_offset(vaddr);
k = __pmd_offset(vaddr);
pgd = pgd_base + i;
for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
pud = (pud_t *)pgd;
for ( ; (j < PTRS_PER_PUD) && (vaddr < end); pud++, j++) {
pmd = (pmd_t *)pud;
for (; (k < PTRS_PER_PMD) && (vaddr < end); pmd++, k++) {
if (pmd_none(*pmd)) {
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd((unsigned long)pte));
BUG_ON(pte != pte_offset_kernel(pmd, 0));
}
vaddr += PMD_SIZE;
}
k = 0;
}
j = 0;
}
#endif
}
[arch/mips/include/asm/pgtable.h]
#ifdef CONFIG_32BIT
#include <asm/pgtable-32.h>
#endif
#ifdef CONFIG_64BIT
#include <asm/pgtable-64.h>
#endif
#define __pgd_offset(address) pgd_index(address)
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) /
/PGDIR_SHIFT=22, 右移22bit,即取最高10bit,得到page gobal directory index
#define __pud_offset(address) (((address) >> PUD_SHIFT) & (PTRS_PER_PUD-1)) /
/#define PTRS_PER_PUD 1, 得到page upper directory,这里pud显然不存在
#define __pmd_offset(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
//#define PTRS_PER_PMD 1
这里是 10bit pgd + 10bit pmd +12bit offset
init_mm.pgd----------> +--------------+ 4k +----------→ +--------------+ 4k +---------→--------+ 4k
| pgd(4byte) | | | pte(4byte) | | |
+--------------+ | +--------------+ | |
| .... | 1024项 | | .... | 1024项 | |
+------------- + | +------------- -+ | |
offset +--------→ | pgd(4byte) | -------------+ +---→ | pte(4byte) | -----------+ +---------→ |
| +--------------+ | +---------------+ | +----
| | |
| | |
10bit ---+ 10bit ---+ 12bit--------+
高端内存
Documentation/vm/highmem.txt中关于高端内存的介绍:
High memory (highmem) is used when the size of physical memory approaches or exceeds the maximum size of virtual memory. At that point it becomes impossible for the kernel to keep all of the available physical memory mapped at all times.This means the kernel needs to start using temporary mappings of the pieces of physical memory that it wants to access.
The traditional split for architectures using this approach is 3:1, 3GiB for
userspace and the top 1GiB for kernel space:
+--------+ 0xffffffff
| Kernel |
+--------+ 0xc0000000
| |
| User |
| |
+--------+ 0x00000000
This means that the kernel can at most map 1GiB of physical memory at any one time, but because we need virtual address space for other things - including temporary maps to access the rest of the physical memory - the actual direct map will typically be less (usually around ~896MiB).
对应mips cpu,2G用于内核地址空间:
+--------+ 0xffffffff
| |
| Kernel |
+--------+ 0x80000000
| User |
| |
+--------+ 0x00000000
obj-$(CONFIG_HIGHMEM) += highmem.o
min_low_pfn:最小低端内存
max_low_pfn:最大低端内存
max_pfn: 最大物理页框
highstart_pfn:高端内存其实页框
highend_pfn:高端内存结束页框
max_mapnr:最大映射页框
#ifdef CONFIG_HIGHMEM
#ifdef CONFIG_DISCONTIGMEM
#error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet"
#endif
max_mapnr = highend_pfn ? highend_pfn : max_low_pfn;
#else
max_mapnr = max_low_pfn;
#endif
如果CONFIG_HIGHMEM选项不打开,max_mapnr = max_low_pfn,则HIGHMEM_START以上的物理内存将不能访问,这样就会造成RAM浪费。

Fix-mapped linear addresses(简称Fixed mappings)包括temporary kernel mappings 和coherent kmap
temporary kernel mappings建立映射:void *__kmap_atomic(struct page *page)
temporary kernel mappings撤销映射:void __kunmap_atomic(void *kvaddr)
temporary kernel mappings不可阻塞
coherent kmap建立映射:void *kmap_coherent(struct page *page, unsigned long addr)
coherent kmap撤销映射:void kunmap_coherent(void)
persistent kernel mappings(简称Permanent kmaps)建立映射:void *kmap(struct page *page)
persistent kernel mappings撤销映射:void kunmap(struct page *page)
persistent kernel mappings可阻塞
#define HIGHMEM_START (UPPERMEM_START + (BRCM_MAX_UPPER_MB << 20))
#define UPPERMEM_START _AC(0x20000000, UL)
如果BRCM_HAS_XKS01不支持,即kseg0只支持512M
#define BRCM_MAX_UPPER_MB _AC(0, UL), 则 HIGHMEM_START 从 0x20000000 即512M就算高端内存
#define MAP_BASE _AC(0xc0000000, UL) , 此时内核态MMU映射从0xc0000000开始
如果BRCM_HAS_XKS01支持,即kseg0支持达到1024M
#define BRCM_MAX_UPPER_MB _AC(768, UL) , 则 HIGHMEM_START从0x20000000+768M 即512M+768M开始算高端内存
#define MAP_BASE _AC(0xe0000000, UL) , 此时内核态MMU映射从0xe0000000开始
void __init kmap_init(void)
{
unsigned long kmap_vstart;
/* cache the first kmap pte */
kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
//起始fix map 对应的pte
}