分类: LINUX
2014-06-04 16:38:06
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
#define DEFINE_PER_CPU(type, name) \
__typeof__(type) per_cpu__##name
所以这里展开来就是 per_cpu__mmu_gathers
extern void _stext, _etext, __data_start, _end;
extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; //PTRS_PER_PGD = 2048
/*
* empty_zero_page is a special page that is used for
* zero-initialized data and COW.
*/
struct page *empty_zero_page;
/*
* The pmd table for the upper-most set of pages.
*/
pmd_t *top_pmd;
//对应下面的静态数组(struct cachepolicy)
#define CPOLICY_UNCACHED 0
#define CPOLICY_BUFFERED 1
#define CPOLICY_WRITETHROUGH 2
#define CPOLICY_WRITEBACK 3
#define CPOLICY_WRITEALLOC 4
static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; //策略索引
static unsigned int ecc_mask __initdata = 0;
pgprot_t pgprot_user;
pgprot_t pgprot_kernel;
EXPORT_SYMBOL(pgprot_user);
EXPORT_SYMBOL(pgprot_kernel);
struct cachepolicy {
const char policy[16]; //策略名
unsigned int cr_mask;
unsigned int pmd;
unsigned int pte;
};
//内存的操作方式(写穿,写回) P560
static struct cachepolicy cache_policies[] __initdata = {
{
.policy = "uncached",
.cr_mask = CR_W|CR_C, // CP15的CR1(P539)
.pmd = PMD_SECT_UNCACHED,
.pte = 0,
}, {
.policy = "buffered",
.cr_mask = CR_C, //1-D cache enable(标记这个为关掉D-cache)
.pmd = PMD_SECT_BUFFERED,
.pte = PTE_BUFFERABLE,
}, {
.policy = "writethrough",
.cr_mask = 0,
.pmd = PMD_SECT_WT,
.pte = PTE_CACHEABLE,
}, {
.policy = "writeback",
.cr_mask = 0,
.pmd = PMD_SECT_WB,
.pte = PTE_BUFFERABLE|PTE_CACHEABLE,
}, {
.policy = "writealloc",
.cr_mask = 0,
.pmd = PMD_SECT_WBWA,
.pte = PTE_BUFFERABLE|PTE_CACHEABLE,
}
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//修改CP15的寄存器control,以及默认的策略
static void __init early_cachepolicy(char **p)
{
int i;
//循环上面的那个数组
for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
int len = strlen(cache_policies[i].policy); //策略名长度
if (memcmp(*p, cache_policies[i].policy, len) == 0) {
//是指定的策略
cachepolicy = i; //根据输入的策略名获取策略索引
//关于这两个全局变量,则需要仔细回读arch/arm/kernal/entry_common.s
cr_alignment &= ~cache_policies[i].cr_mask;
cr_no_alignment &= ~cache_policies[i].cr_mask; //去除属性
*p += len; //跨过本策略的字符串
break;
}
}
if (i == ARRAY_SIZE(cache_policies)) //没有找到对应的策略,打印错误信息
printk(KERN_ERR "ERROR: unknown or unsupported cache policy\n");
if (cpu_architecture() >= CPU_ARCH_ARMv6) {
printk(KERN_WARNING "Only cachepolicy=writeback supported on ARMv6 and later\n");
cachepolicy = CPOLICY_WRITEBACK;
}
flush_cache_all(); //将设置从cache中刷入内存
set_cr(cr_alignment); //操作CP15的control寄存器(P539)
}
__early_param("cachepolicy=", early_cachepolicy);
#define __early_param(name,fn) \
static struct early_params __early_##fn __used \
__attribute__((__section__(".early_param.init"))) = { name, fn }
将本函数放入指定段。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//设置策略为buffered(关了CACHE)
static void __init early_nocache(char **__unused)
{
char *p = "buffered";
printk(KERN_WARNING "nocache is deprecated; use cachepolicy=%s\n", p);
early_cachepolicy(&p); //设置”buffered”策略对应的CP15 control寄存器
//由静态数组可知,初始值这个策略是关了D-CACHE
}
__early_param("nocache", early_nocache);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//同上
static void __init early_nowrite(char **__unused)
{
char *p = "uncached";
printk(KERN_WARNING "nowb is deprecated; use cachepolicy=%s\n", p);
early_cachepolicy(&p);
}
__early_param("nowb", early_nowrite);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static void __init early_ecc(char **p)
{
//ecc_mask在本文开头定义,初始值为0
if (memcmp(*p, "on", 2) == 0) {
ecc_mask = PMD_PROTECTION; //1<<9
*p += 2;
} else if (memcmp(*p, "off", 3) == 0) {
ecc_mask = 0;
*p += 3;
}
}
__early_param("ecc=", early_ecc);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static int __init noalign_setup(char *__unused)
{
cr_alignment &= ~CR_A;
cr_no_alignment &= ~CR_A; //不检查地址对齐
set_cr(cr_alignment);
return 1;
}
__setup("noalign", noalign_setup);
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//修改CP15的CR2寄存器
void adjust_cr(unsigned long mask, unsigned long set)
{
unsigned long flags;
mask &= ~CR_A; //不检查地址对齐
set &= mask;
local_irq_save(flags); //关中断
cr_no_alignment = (cr_no_alignment & ~mask) | set; //带上地址检查
cr_alignment = (cr_alignment & ~mask) | set;
set_cr((get_cr() & ~mask) | set);
local_irq_restore(flags); //恢复中断
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#define PROT_PTE_DEVICE L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_WRITE
(1<<0) | (1<<1) | (1<<7) | (1<<5)
#define PROT_SECT_DEVICE PMD_TYPE_SECT|PMD_SECT_XN|PMD_SECT_AP_WRITE
(2<<0) | (1<<4) | (1<<10) //段描述
static struct mem_type mem_types[] = {}; //内存类型数组
struct mem_type {
unsigned int prot_pte;
unsigned int prot_pte_ext;
unsigned int prot_l1;
unsigned int prot_sect;
unsigned int domain;
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//根据输入的type,返回该type对应的mem_types[]内存信息。
const struct mem_type *get_mem_type(unsigned int type)
{
return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//建立内存类型表
static void __init build_mem_type_table(void)
{
struct cachepolicy *cp; //策略(本文件开始)
unsigned int cr = get_cr(); //获取CP15中的control寄存器
unsigned int user_pgprot, kern_pgprot;
int cpu_arch = cpu_architecture(); //返回CPU类型
int i;
if (cpu_arch < CPU_ARCH_ARMv5) {
// cachepolicy为策略索引,本文件一开始就初始为CPOLICY_WRITEBACK
if (cachepolicy >= CPOLICY_WRITEALLOC)
cachepolicy = CPOLICY_WRITEBACK; //超过最大值,设置为该状态
ecc_mask = 0; //因为v5前的处理器的一级描述符没有定义第9位作为保护标志位
}
if (cpu_is_xscale()) {
for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
mem_types[i].prot_sect &= ~PMD_BIT4;
mem_types[i].prot_l1 &= ~PMD_BIT4;
}
}
else if (cpu_arch < CPU_ARCH_ARMv6)
{
//如果prot_ll和prot_sect属性有设置,则将其bit4写1。(这两个寄存器都是一级页表描述符,bit4固定为1。见规格书560页)
for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
if (mem_types[i].prot_l1) mem_types[i].prot_l1 |= PMD_BIT4;
if (mem_types[i].prot_sect) mem_types[i].prot_sect |= PMD_BIT4;
}
}
// cache_policies在本文件的一开始有设置为全局变量数组
//cachepolicy初始为WRITEBACK(本文件的开始),在early_cachepolicy函数中可由输入的策略名改变
cp = &cache_policies[cachepolicy]; //根据策略号获取详细的策略信息
//如果是默认的,则为:
{
.policy = "writeback",
.cr_mask = 0,
.pmd = PMD_SECT_WB,
.pte = PTE_BUFFERABLE|PTE_CACHEABLE,
}
//这两个寄存器是内核和用户的二级页表描述符
kern_pgprot = user_pgprot = cp->pte; //获取策略中的pte属性
//删除了高端版本的代码
//修改protection_map中关于BC的设置为user_pgprot中的设置(二级页表中使用)
for (i = 0; i < 16; i++) {
unsigned long v = pgprot_val(protection_map[i]);
v = (v & ~(L_PTE_BUFFERABLE|L_PTE_CACHEABLE)) | user_pgprot;
protection_map[i] = __pgprot(v);
}
//给指定的这两个内存类型加上kern_pgprot的属性(一级页表的BC描述)
mem_types[MT_LOW_VECTORS].prot_pte |= kern_pgprot;
mem_types[MT_HIGH_VECTORS].prot_pte |= kern_pgprot;
//指定内存类型中,prot_sect的bit12写0(在ARM9中该位未使用)
mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1);
//给二级页表分别加入指定属性,注意,此时user_pgprot和kern_pgprot是一样的
pgprot_user = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
L_PTE_DIRTY | L_PTE_WRITE |
L_PTE_EXEC | kern_pgprot);
//ecc_mask在V5以下固定为0
//cp->pmd = PMD_SECT_WB,即带有BC属性
mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
mem_types[MT_MEMORY].prot_sect |= ecc_mask | cp->pmd;
mem_types[MT_ROM].prot_sect |= cp->pmd;
switch (cp->pmd) {
case PMD_SECT_WT:
mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT;
break;
case PMD_SECT_WB:
case PMD_SECT_WBWA:
mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
break;
}
//以上所有的操作都是为了给mem_types这个结构体中的各种类型中的页表参数添加上我们的要求,主要是一级页表,二级页表,ap(访问权限控制);至于domain是利用系统初始化时的值,不用我们再进行干预。
//系统的domain类型一共有四种,kernel——0;user——1;io——2
printk("Memory policy: ECC %sabled, Data cache %s\n",
ecc_mask ? "en" : "dis", cp->policy);
//给段描述符加上权限
for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
struct mem_type *t = &mem_types[i];
if (t->prot_l1) t->prot_l1 |= PMD_DOMAIN(t->domain); ;; x<<5
if (t->prot_sect) t->prot_sect |= PMD_DOMAIN(t->domain);
}
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这一段的代码,建议从后面create_mapping函数看起
#define vectors_base() (vectors_high() ? 0xffff0000 : 0) //中断向量表地址
//申请PTE描述符,并初始他。
//如果不看申请函数,那么这个函数和ipremap.c文件中的remap_area_pte函数功能一样
//实际这个函数没有调用
static void __init alloc_init_pte(
pmd_t *pmd, //段码
unsigned long addr, //开始虚拟地址
unsigned long end, //结束地址
unsigned long pfn, //物理页码
const struct mem_type *type) //内存类型
{
pte_t *pte;
//pmd为0,没有对应的段号
//这里不太可能满足
if (pmd_none(*pmd)) {
pte = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));
// PTRS_PER_PTE = 512,即一个PTE描述512页,有两个PTE(内核一个(2~4k),MMU一个(0~2k),乘pte格式的大小,也就是PTE表所需要的空间)
//从0开始申请一块内存
__pmd_populate(pmd, __pa(pte) | type->prot_l1);
//给PMD挂上PTE(注意有两个PTE)
static inline void __pmd_populate(pmd_t *pmdp, unsigned long pmdval)
{
pmdp[0] = __pmd(pmdval); //第一个PTE
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); //第二个PTE
//补充:一个PTE应该是256项,也就是一个段是1M,只是处理的时候,总是连续处理两个段,也就是2M的空间,所以给我们一种一个PTE是512的错觉。
flush_pmd_entry(pmdp); //内容强行刷进RAM中
}
}
pte = pte_offset_kernel(pmd, addr);
//这个宏在ipremap文件中有分析过,pmd给出一级页表的高地址,addr提供二级页表的地址,最后返回二级页表的物理地址(根据MMU的返回)
do {
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)),
type->prot_pte_ext); //设置PTE的值
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//从函数名可以看出,申请并初始化一个段表
//这个函数在create_map中是用一个do_while循环以段为单位调用的,只有在结束可能有非段对齐(在create_map函数中,如果地址没有段对齐,是出错的)
//通过这个函数的分析,我们可以知道一级页表管理数组中的值为对应的段描述符号(对应2410规格书的P560页)
static void __init alloc_init_section(
pgd_t *pgd, //段码
unsigned long addr, //虚拟地址
unsigned long end, //结束地址
unsigned long phys, //物理地址
const struct mem_type *type) //内存类型
{
pmd_t *pmd = pmd_offset(pgd, addr); //直接将pdg给pmd
//addr, end, phys都是1M对齐,则直接进行段映射(大部分情况下应该是满足条件)
if (((addr | end | phys) & ~SECTION_MASK) == 0) {
pmd_t *p = pmd;
if (addr & SECTION_SIZE)
pmd++; //奇数段(段的处理总是2段一起处理的)
do {
//物理地址 | 段属性à填充进段描述符中。(2410规格书P560)
*pmd = __pmd(phys | type->prot_sect);
phys += SECTION_SIZE; //下一段(1M)
} while (pmd++, addr += SECTION_SIZE, addr != end);
//这个循环只运行一次
flush_pmd_entry(p); //同步数据进RAM(由于有cache机制,所以数据操作的时候是先保存在cache中的,这里是强制将数据从cache中刷进RAM中)
} else {
//没有按1M对齐,直接初始化二级页表(实现是上一个函数)
//一般函数调用不到,因为在本函数的调用函数中,要求开始地址和长度是段对齐的。调用到这个函数的情况只有可能是大小不是段对齐时,将多出的部分给映射了
alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
}
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在下面的函数后,当页表号大于1M,即寻址超过4G的时候会调用这个函数
//在ARM中一般不会调用这个函数
static void __init create_36bit_mapping(
struct map_desc *md, //静态映射
const struct mem_type *type)
{
unsigned long phys, addr, length, end;
pgd_t *pgd;
addr = md->virtual; //虚拟地址
phys = (unsigned long)__pfn_to_phys(md->pfn); //从页获取物理地址
length = PAGE_ALIGN(md->length); //页对齐的长度
//静态映射的权限必须是Kernel,否则出错
if (type->domain) {
printk(KERN_ERR "MM: invalid domain in supersection "
"mapping for 0x%08llx at 0x%08lx\n",
__pfn_to_phys((u64)md->pfn), addr);
return;
}
//地址必须是16M对齐
if ((addr | length | __pfn_to_phys(md->pfn)) & ~SUPERSECTION_MASK) {
printk(KERN_ERR "MM: cannot create mapping for "
"0x%08llx at 0x%08lx invalid alignment\n",
__pfn_to_phys((u64)md->pfn), addr);
return;
}
/*
* Shift bits [35:32] of address into bits [23:20] of PMD
* (See ARMv6 spec).
*/
phys |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
pgd = pgd_offset_k(addr); //获取相对于init_mm的1M偏移
end = addr + length; //计算结束地址
//为一级页表加上prot_sect属性和bit18 = 1(实际只有ARM V6以上才有效)
do {
pmd_t *pmd = pmd_offset(pgd, addr); //*pgd
int i;
//由于前面限制了是16M对齐,所以这里是16次循环
for (i = 0; i < 16; i++)
*pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER);
addr += SUPERSECTION_SIZE;
phys += SUPERSECTION_SIZE;
pgd += SUPERSECTION_SIZE >> PGDIR_SHIFT;
} while (addr != end);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//创建内存映射,这个函数的核心函数是alloc_init_section,其他大段大段的内容都是参数的保护。
//在start_kernel对内存初始化的时候调用,参数的传入见本文件夹下init.c的map_memory_bank函数
//实际作用是创建bank内存描述的内存范围的一级页表
map.pfn = __phys_to_pfn(virt_to_phys(vectors)); //物理页框号
map.virtual = 0xffff0000; //物理地址对应的虚拟地址
map.length = PAGE_SIZE; //bank大小
map.type = MT_HIGH_VECTORS; //类型
void __init create_mapping(struct map_desc *md)
{
unsigned long phys, addr, length, end;
const struct mem_type *type;
pgd_t *pgd;
//虚拟地址不是中断表地址 && 虚拟地址在用户区(0~3G)à出错
if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
printk(KERN_WARNING "BUG: not creating mapping for "
"0x%08llx at 0x%08lx in user region\n",
__pfn_to_phys((u64)md->pfn), md->virtual);
return; //返回
}
//内存类型 = IO型 或 ROM
//虚拟地址为内存申请区(3G~3.5G)
//打印警告
if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {
printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx "
"overlaps vmalloc space\n", __pfn_to_phys((u64)md->pfn), md->virtual);
}
//根据内存类型获取内存信息(主要是CP15中的内存操作权限)
type = &mem_types[md->type];
//页表号>1M,超过4G了(实际上这个情况已经超过普通嵌入式的应用了)
if (md->pfn >= 0x100000) {
create_36bit_mapping(md, type); //调用36位的内存映射
return;
}
addr = md->virtual & PAGE_MASK; //获取4K页对齐虚拟地址
phys = (unsigned long)__pfn_to_phys(md->pfn); //获取物理地址
length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
//按页对齐计算长度(主要是补齐未按页对齐的多余的部分)
//段类型为0(出错)且参数未按段对齐,出错
if (type->prot_l1 == 0 &&
((addr | phys | length) & ~SECTION_MASK)) {
printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not "
"be mapped using pages, ignoring.\n", __pfn_to_phys(md->pfn), addr);
return;
}
//以上部分只是参数的保护,无实际意义
pgd = pgd_offset_k(addr); //一级数组中addr对应的段在init_mm->pgd的下标
end = addr + length; //计算结束地址
do {
unsigned long next = pgd_addr_end(addr, end); //获得下一段开始地址
//申请并初始化一个段
//段码,虚拟地址,结束地址,物理地址,内存类型
alloc_init_section(pgd, addr, next, phys, type);
phys += next - addr; //物理地址累加
addr = next; //地址为下一段的开始地址
} while (pgd++, addr != end); //下一段,地址不等于结束地址
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//根据内存描述符映射内存(也就是创建一级映射表,所以平台上的资源不需要ioremap就可以直接用,而非平台资源则需要申请后进行一次ioremap)
void __init iotable_init(
struct map_desc *io_desc, //内存描述符
int nr)
{
int i;
for (i = 0; i < nr; i++)
create_mapping(io_desc + i);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//清除全部的段映射设置(即16K一级页表全部写0)
static inline void prepare_page_table(struct meminfo *mi)
{
unsigned long addr;
//MODULE_START为3G内核之前的16M地址
for (addr = 0; addr < MODULE_START; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
//pmd_clear是将()中的页表项地址中的Cache无效,以及清除段开始的两个字节
// pmd_off_k为获取addr在一级页表中的位置
//挖一下这个函数:
static inline pmd_t *pmd_off_k(unsigned long virt)
{
return pmd_off(pgd_offset_k(virt), virt); //段号,地址
}
#define pgd_index(addr) ((addr) >> PGDIR_SHIFT) //根据地址取段号
#define pgd_offset(mm, addr) ((mm)->pgd+pgd_index(addr)) //pgd为段描述符首址,根据段号获取段描述符
#define pgd_offset_k(addr) pgd_offset(&init_mm, addr) //获取init_mm的段描述符
//init_mm在INIT_MM中定义为:
.pgd = swapper_pg_dir
#define swapper_pg_dir ((pgd_t *) 0)
//所以这里操作的是从0开始的描述符地址
static inline pmd_t *pmd_off(pgd_t *pgd, unsigned long virt)
{
return pmd_offset(pgd, virt);
}
#define pmd_offset(dir, addr) ((pmd_t *)(dir))
- -! 无语…..,挖了半天,就是获取段号
#define pmd_clear(pmdp) \
do { \
pmdp[0] = __pmd(0); \
pmdp[1] = __pmd(0); \
clean_pmd_entry(pmdp); \
} while (0)
static inline void clean_pmd_entry(pmd_t *pmd)
{
const unsigned int __tlb_flag = __cpu_tlb_flags;
if (tlb_flag(TLB_DCLEAN))
asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd" //清除输出缓存中的某块
: : "r" (pmd) : "cc");
}
//上面的addr到模块结束,这里是从模块区开始到页偏移区(3G),也就是16M的空间
for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
//越过内存信息区开始,到VMALLOC区结束
//初始化的时候,meminfo的值由mem命令行给进
for (addr = __phys_to_virt(mi->bank[0].start + mi->bank[0].size);
addr < VMALLOC_END;
addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//保留一部分内存不被动态分布(内存开始处的一级页表区,和程序代码区)
void __init reserve_node_zero(pg_data_t *pgdat)
{
unsigned long res_size = 0;
//段信息,代码段开始的物理地址,大小
//将代码段部分的内存标记为已占用,不能被动态分配
//函数的实现在mm\ bootmem.c文件中
reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext);
//swapper_pg_dir为0
//这里是保留开始的2K * 8大小的一级页表区
reserve_bootmem_node(pgdat, __pa(swapper_pg_dir),
PTRS_PER_PGD * sizeof(pgd_t));
//这里res_size为0,保留其他的数据
if (res_size)
reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size);
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//设备IO内存创建映射
//实际操作是:申请了一页内存来映射中断向量表,然后调用机器信息中的map_io函数注册所有模块的时钟
static void __init devicemaps_init(struct machine_desc *mdesc)
{
struct map_desc map;
unsigned long addr;
void *vectors;
//为中断向量分配一个物理页面:4K
vectors = alloc_bootmem_low_pages(PAGE_SIZE);
BUG_ON(!vectors);
//清除高端内存的段映射表对应的一级页表(3.5G~4G,非VMALLOC区)
for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
//为中断向量创建映射,把分配的物理页面映射到0xffff 0000或0x0000 0000
map.pfn = __phys_to_pfn(virt_to_phys(vectors)); //获取物理地址的页号
map.virtual = 0xffff0000; //虚拟地址
map.length = PAGE_SIZE; //大小:4K
map.type = MT_HIGH_VECTORS; //内存类型:高端向量映射
create_mapping(&map); //根据这些信息创建映射(实际就是建立一级页表和二级页表)
//建议此时再回头看下create_mapping函数,加深理解
//中断向量不在高端,而在0,重新建立一次映射(实际中断向量是在高端)
if (!vectors_high()) {
map.virtual = 0;
map.type = MT_LOW_VECTORS;
create_mapping(&map);
}
//调用机器描述块machine_desc中的map_io函数,完成设备IO内存的映射
//实际调用的是utu2440_map_io,见“平台信息”,大概操作是:
//初始化IO,初始化时钟,初始化UART,注册板子上所有模块的时钟
if (mdesc->map_io) mdesc->map_io();
//将缓存中的内容更新到RAM中
local_flush_tlb_all();
flush_cache_all();
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//在start_kernel的setup_arch函数中调用
void __init paging_init(
struct meminfo *mi, //内存的描述信息
struct machine_desc *mdesc) //机器信息,见“平台信息”
{
void *zero_page;
build_mem_type_table(); //根据MEM的策略补充页表的属性
prepare_page_table(mi); //清除段映射(16K一级页表,从地址0开始的)
bootmem_init(mi); //实现在arch\mm\init.c中,初始化bootmem分配信息
//实际上是初始化引导阶段所使用的内存,当内核启动完成,将使用SLUB来管理内存,届时管理方法将和boot阶段会不一样。
devicemaps_init(mdesc); //映射中断向量表(一、二级),注册模块时钟
top_pmd = pmd_off_k(0xffff0000); //获取中断向量的一级页表
//在低端内存中申请一页内存并清0,注意返回的是虚拟地址
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
memzero(zero_page, PAGE_SIZE);
//获取该虚拟地址对应的页描述符,注意empty_zero_page是全局变量
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page); //刷新D-CAHCE内容进RAM中
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void setup_mm_for_reboot(char mode)
{
unsigned long base_pmdval;
pgd_t *pgd;
int i;
//获取段
if (current->mm && current->mm->pgd)
pgd = current->mm->pgd;
else
pgd = init_mm.pgd;
//设置段属性(段,可读写)
base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT;
//设置段属性(bit4 固定为1,见2410规格书560页)
if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
base_pmdval |= PMD_BIT4;
//整个一级页表(范围为用户内存区)
for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++, pgd++) {
//设置一级页表的值(P560)
unsigned long pmdval = (i << PGDIR_SHIFT) | base_pmdval;
pmd_t *pmd;
pmd = pmd_off(pgd, i << PGDIR_SHIFT); //段偏移
pmd[0] = __pmd(pmdval); //[0]为一级页表描述
pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); //[1]为下一段的一级页表
flush_pmd_entry(pmd); //设置值刷进RAM中
//从这里的设置也可以看出,段是一次设置两个段的。
}
}