arch\arm\mm\mmu.c

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

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,            // CP15CR1P539

              .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);     //操作CP15control寄存器(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);

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//修改CP15CR2寄存器

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_llprot_sect属性有设置,则将其bit41。(这两个寄存器都是一级页表描述符,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_sectbit120(ARM9中该位未使用)

       mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1);

 

       //给二级页表分别加入指定属性,注意,此时user_pgprotkern_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_maskV5以下固定为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——0user——1io——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;

 

       //pmd0,没有对应的段号

       //这里不太可能满足

       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的空间,所以给我们一种一个PTE512的错觉。

       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);           //直接将pdgpmd

 

       //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_mm1M偏移

       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.cmap_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_START3G内核之前的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_mmINIT_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_dir0

       //这里是保留开始的2K * 8大小的一级页表区

       reserve_bootmem_node(pgdat,     __pa(swapper_pg_dir),

                          PTRS_PER_PGD * sizeof(pgd_t));

 

       //这里res_size0,保留其他的数据

       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 00000x0000 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_kernelsetup_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

              //从这里的设置也可以看出,段是一次设置两个段的。

       }

}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值