感谢支持
我们一直在努力

Linux内核高端内存管理之固定内存区与映射

前面(见 http://www.linuxidc.com/Linux/2012-02/53457.htm 与 http://www.linuxidc.com/Linux/2012-02/53458.htm)总结了高端内存中永久内核映射和临时内核映射。linux高端内存中的临时内存区为固定内存区的一部分,下面是Linux内存布局图


Linux内核高端内存管理之固定内存区与映射


对于固定内存在linux内核中有下面描述

[cpp]


  1. enum fixed_addresses {  
  2. #ifdef CONFIG_X86_32   
  3.     FIX_HOLE,  
  4.     FIX_VDSO,  
  5. #else   
  6.     VSYSCALL_LAST_PAGE,  
  7.     VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE  
  8.                 + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) – 1,  
  9.     VSYSCALL_HPET,  
  10. #endif   
  11.     FIX_DBGP_BASE,  
  12.     FIX_EARLYCON_MEM_BASE,  
  13. #ifdef CONFIG_X86_LOCAL_APIC   
  14.     FIX_APIC_BASE,  /* local (CPU) APIC) — required for SMP or not */  
  15. #endif   
  16. #ifdef CONFIG_X86_IO_APIC   
  17.     FIX_IO_APIC_BASE_0,  
  18.     FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS – 1,  
  19. #endif   
  20. #ifdef CONFIG_X86_VISWS_APIC   
  21.     FIX_CO_CPU, /* Cobalt timer */  
  22.     FIX_CO_APIC,    /* Cobalt APIC Redirection Table */  
  23.     FIX_LI_PCIA,    /* Lithium PCI Bridge A */  
  24.     FIX_LI_PCIB,    /* Lithium PCI Bridge B */  
  25. #endif   
  26. #ifdef CONFIG_X86_F00F_BUG   
  27.     FIX_F00F_IDT,   /* Virtual mapping for IDT */  
  28. #endif   
  29. #ifdef CONFIG_X86_CYCLONE_TIMER   
  30.     FIX_CYCLONE_TIMER, /*cyclone timer register*/  
  31. #endif   
  32. #ifdef CONFIG_X86_32   
  33.     FIX_KMAP_BEGIN, /* reserved pte’s for temporary kernel mappings */  
  34.     FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,  
  35. #ifdef CONFIG_PCI_MMCONFIG   
  36.     FIX_PCIE_MCFG,  
  37. #endif   
  38. #endif   
  39. #ifdef CONFIG_PARAVIRT   
  40.     FIX_PARAVIRT_BOOTMAP,  
  41. #endif   
  42.     FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */  
  43.     FIX_TEXT_POKE0, /* first page is last, because allocation is backward */  
  44.     __end_of_permanent_fixed_addresses,  
  45.     /* 
  46.      * 256 temporary boot-time mappings, used by early_ioremap(), 
  47.      * before ioremap() is functional. 
  48.      * 
  49.      * We round it up to the next 256 pages boundary so that we 
  50.      * can have a single pgd entry and a single pte table: 
  51.      */  
  52. #define NR_FIX_BTMAPS       64   
  53. #define FIX_BTMAPS_SLOTS    4   
  54.     FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 256 –  
  55.             (__end_of_permanent_fixed_addresses & 255),  
  56.     FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_SLOTS – 1,  
  57. #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT   
  58.     FIX_OHCI1394_BASE,  
  59. #endif   
  60. #ifdef CONFIG_X86_32   
  61.     FIX_WP_TEST,  
  62. #endif   
  63. #ifdef CONFIG_INTEL_TXT   
  64.     FIX_TBOOT_BASE,  
  65. #endif   
  66.     __end_of_fixed_addresses  
  67. };  

固定映射


ioremap的作用是将IOBIOS以及物理地址空间映射到在896M1G128M的地址空间内,使得kernel能够访问该空间并进行相应的读写操作。


start_kernel()->setup_arch()->early_ioremap_init()

[cpp]


  1. void __init early_ioremap_init(void)  
  2. {  
  3.     pmd_t *pmd;  
  4.     int i;  
  5.   
  6.     if (early_ioremap_debug)  
  7.         printk(KERN_INFO “early_ioremap_init()\n”);  
  8.     /*将fixed_address里的索引的虚拟地址放入slot_virt 
  9.     ,从代码里面可以看出,放入slot_virt中得虚拟地址为1M*/  
  10.     for (i = 0; i < FIX_BTMAPS_SLOTS; i++)  
  11.         slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN – NR_FIX_BTMAPS*i);  
  12.   
  13.     /*得到固定映射区的pmd 
  14.     ,此pmd为虚拟地址转换为物理地址的pmd*/  
  15.     pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));  
  16.     memset(bm_pte, 0, sizeof(bm_pte));  
  17.     /*将bm_pte页表设置为固定映射区开始地址的pmd的第一个页表;*/  
  18.     pmd_populate_kernel(&init_mm, pmd, bm_pte);  
  19.   
  20.     /* 
  21.      * The boot-ioremap range spans multiple pmds, for which 
  22.      * we are not prepared: 
  23.      */  
  24.      /*系统要求所有的ioremap映射在一个pmd上,超出这个pmd将警告*/  
  25.     if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) {  
  26.         WARN_ON(1);  
  27.         printk(KERN_WARNING “pmd %p != %p\n”,  
  28.                pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END)));  
  29.         printk(KERN_WARNING “fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n”,  
  30.             fix_to_virt(FIX_BTMAP_BEGIN));  
  31.         printk(KERN_WARNING “fix_to_virt(FIX_BTMAP_END):   %08lx\n”,  
  32.             fix_to_virt(FIX_BTMAP_END));  
  33.   
  34.         printk(KERN_WARNING “FIX_BTMAP_END:       %d\n”, FIX_BTMAP_END);  
  35.         printk(KERN_WARNING “FIX_BTMAP_BEGIN:     %d\n”,  
  36.                FIX_BTMAP_BEGIN);  
  37.     }  
  38. }  

[cpp]


  1. static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata;  

[cpp]


  1. #define __fix_to_virt(x)    (FIXADDR_TOP – ((x) << PAGE_SHIFT))  

其中FIXADDR_TOP4G-4K

对于ioremap的使用需要通过early_memremapearly_iounmap进行。由于对应于ioremap的内存空间是有限的,所以对于ioremap空间的使用遵照使用结束马上释放的原则。这就是说early_memremapearly_iounmap必须配对使用并且访问结束必须马上执行unmap

[cpp]


  1. static void __init __iomem *  
  2. __early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot)  
  3. {  
  4.     unsigned long offset;  
  5.     resource_size_t last_addr;  
  6.     unsigned int nrpages;  
  7.     enum fixed_addresses idx0, idx;  
  8.     int i, slot;  
  9.   
  10.     WARN_ON(system_state != SYSTEM_BOOTING);  
  11.   
  12.     slot = -1;  
  13.     /*pre_map[]是一个索引与slot_virt[]一一对应,这段for 
  14.     的含义在于找到一个没有被使用过的slot_virt[i]的页面, 
  15.     该slot_virt[i]所指向的虚拟页面地址就是将会和实际物 
  16.     理地址phys_addr相绑定的虚拟地址。*/  
  17.     for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {  
  18.         if (!prev_map[i]) {  
  19.             slot = i;  
  20.             break;  
  21.         }  
  22.     }  
  23.   
  24.     if (slot < 0) {  
  25.         printk(KERN_INFO “early_iomap(%08llx, %08lx) not found slot\n”,  
  26.              (u64)phys_addr, size);  
  27.         WARN_ON(1);  
  28.         return NULL;  
  29.     }  
  30.   
  31.     if (early_ioremap_debug) {  
  32.         printk(KERN_INFO “early_ioremap(%08llx, %08lx) [%d] => “,  
  33.                (u64)phys_addr, size, slot);  
  34.         dump_stack();  
  35.     }  
  36.   
  37.     /* Don’t allow wraparound or zero size */  
  38.     last_addr = phys_addr + size – 1;  
  39.     if (!size || last_addr < phys_addr) {  
  40.         WARN_ON(1);  
  41.         return NULL;  
  42.     }  
  43.   
  44.     prev_size[slot] = size;  
  45.     /* 
  46.      * Mappings have to be page-aligned 
  47.      */  
  48.     offset = phys_addr & ~PAGE_MASK;/*offset是页内的偏移*/  
  49.     phys_addr &= PAGE_MASK;/*现在phys_addr就是起始页面的地址*/  
  50.     /*现在size就是指出了到底占据了多少个页面的大小*/  
  51.     size = PAGE_ALIGN(last_addr + 1) – phys_addr;  
  52.   
  53.     /* 
  54.      * Mappings have to fit in the FIX_BTMAP area. 
  55.      */  
  56.      /*到底我们需要多少页面*/  
  57.     nrpages = size >> PAGE_SHIFT;  
  58.     if (nrpages > NR_FIX_BTMAPS) {  
  59.         WARN_ON(1);  
  60.         return NULL;  
  61.     }  
  62.   
  63.     /* 
  64.      * Ok, go for it.. 
  65.      */  
  66.      /*找到空闲slot所对应的fixed_address中的索引号*/  
  67.     idx0 = FIX_BTMAP_BEGIN – NR_FIX_BTMAPS*slot;  
  68.     idx = idx0;  
  69.     while (nrpages > 0) {  
  70.         /*在bm_ptes中将指定的idx索引的页表项填充为对应的物理地址使得bm_pte[idx]指向正确的物理页面地址*/  
  71.         early_set_fixmap(idx, phys_addr, prot);  
  72.         phys_addr += PAGE_SIZE;  
  73.         –idx;  
  74.         –nrpages;  
  75.     }  
  76.     if (early_ioremap_debug)  
  77.         printk(KERN_CONT “%08lx + %08lx\n”, offset, slot_virt[slot]);  
  78.     /*返回phys_addr所指向的虚拟地址*/  
  79.     prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]);  
  80.     return prev_map[slot];  
  81. }  

赞(0) 打赏
转载请注明出处:服务器评测 » Linux内核高端内存管理之固定内存区与映射
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏