新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux內核高-低端內存設置代碼跟蹤(ARM構架)

        Linux內核高-低端內存設置代碼跟蹤(ARM構架)

        作者: 時間:2016-11-21 來源:網絡 收藏
        對于ARM中內核如何在啟動的時候設置高低端內存的分界線(也是邏輯地址與虛擬地址分界線(虛擬地址)減去那個固定的偏移),這里我稍微引導下(內核分析使用Linux-3.0):
        首先定位設置內核虛擬地址起始位置(也就是內核邏輯地址末端+1的地址)的文件:init.c (archarmmm),在這個文件中的void __init bootmem_init(void)函數如下
        1. void __init bootmem_init(void)
        2. {
        3. unsigned long min,max_low,max_high;
        4. max_low=max_high=0;
        5. find_limits(&min,&max_low,&max_high);
        6. arm_bootmem_init(min,max_low);
        7. /*
        8. *Sparsemem triestoallocate bootmeminmemory_present(),
        9. *so must be done after the fixed reservations
        10. */
        11. arm_memory_present();
        12. /*
        13. *sparse_init()needs the bootmem allocator upandrunning.
        14. */
        15. sparse_init();
        16. /*
        17. *Nowfree the memory-free_area_init_node needs
        18. *the sparse mem_map arrays initialized by sparse_init()
        19. *formemmap_init_zone(),otherwise all PFNs are invalid.
        20. */
        21. arm_bootmem_free(min,max_low,max_high);
        22. high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1;
        23. /*
        24. *This doesnt seemtobe used by the Linux memory manager any
        25. *more,butisused by ll_rw_block.Ifwe cangetrid of it,we
        26. *alsogetrid of some of the stuff above as well.
        27. *
        28. *Note:max_low_pfnandmax_pfn reflect the number of _pages_in
        29. *the system,notthe maximum PFN.
        30. */
        31. max_low_pfn=max_low-PHYS_PFN_OFFSET;
        32. max_pfn=max_high-PHYS_PFN_OFFSET;
        33. }
        這個high_memory=__va(((phys_addr_t)max_low<<PAGE_SHIFT)-1)+1;語句就是關鍵。從這里可以知道max_low就是高端內存的起始地址(物理地址)。那么這個max_low是如何得到的?其實看上面的代碼可以推測出,他其實是在find_limits(&min,&max_low,&max_high);中(在同一個文件中)被設置的:
        1. static void __init find_limits(unsigned long*min,unsigned long*max_low,
        2. unsigned long*max_high)
        3. {
        4. struct meminfo*mi=&meminfo;
        5. inti;
        6. *min=-1UL;
        7. *max_low=*max_high=0;
        8. for_each_bank(i,mi){
        9. struct membank*bank=&mi->bank[i];
        10. unsigned long start,end;
        11. start=bank_pfn_start(bank);
        12. end=bank_pfn_end(bank);
        13. if(*min>start)
        14. *min=start;
        15. if(*max_high<end)
        16. *max_high=end;
        17. if (bank->highmem)
        18. continue;
        19. if (*max_low < end)
        20. *max_low = end;
        21. }
        22. }
        這個函數的意思很明顯:通過掃描struct meminfo*mi=&meminfo;(結構體meminfo的數組)中的所有信息,設置三個指針所指的變量:
        1. min :內存物理地址起始
        2. max_low :低端內存區物理地址末端
        3. max_high :高端內存區物理地址末端
        從上面可以看出,max_low和max_high所保存的地址不同就是由于bank->highmem造成的,它是內存bank被設為高端內存的依據:
        1. “如果這個內存bank是高端內存(bank->highmem != 0),跳過max_low = end;語句,max_low和max_high將不同(結果實際上是max_high >max_low);
        2. 否則假設沒有一個內存bank是高端內存(所有bank->highmem == 0)max_low和max_high必然一致(高端內存大小為0)”
        當然要實現這個函數的功能,必須保證meminfo所指數組中的所有bank是按照地址數據從小到大排序好的哦~~。但是這個大家不用擔心,后面會看到的:)
        經過上面的跟蹤,焦點集中到了全局變量(同一個文件中):
        1. struct meminfo meminfo;
        這個結構體的定義(setup.h (archarmincludeasm)):
        1. /*
        2. * Memory map description
        3. */
        4. #define NR_BANKS 8
        5. struct membank {
        6. phys_addr_t start;
        7. unsigned long size;
        8. unsigned int highmem;
        9. };
        10. struct meminfo {
        11. int nr_banks;
        12. struct membank bank[NR_BANKS];
        13. };
        14. extern struct meminfo meminfo;
        15. #define for_each_bank(iter,mi)
        16. for (iter = 0; iter < (mi)->nr_banks; iter++)
        #define bank_pfn_start(bank) __phys_to_pfn((bank)->start)
        #define bank_pfn_end(bank) __phys_to_pfn((bank)->start + (bank)->size)
        #define bank_pfn_size(bank) ((bank)->size >> PAGE_SHIFT)
        #define bank_phys_start(bank) (bank)->start
        #define bank_phys_end(bank) ((bank)->start + (bank)->size)
        #define bank_phys_size(bank) (bank)->size
        只要找到初始化這個全局變量并完成排序的地方,就可以知道高端內存是如何配置的了!!OK,明確目標,go on~~~
        通過查找代碼,我們可以在setup.c (archarmkernel)這個文件中找到相關的代碼。在系統啟動早期會運行的函數(具體的順序你可以自行分析下ARM內核的啟動流程,以后我也會寫下)中有這樣一個函數:
        1. void __init setup_arch(char **cmdline_p)
        2. {
        3. struct machine_desc *mdesc;
        4. unwind_init();
        5. setup_processor();
        6. mdesc = setup_machine_fdt(__atags_pointer);
        7. if (!mdesc)
        8. mdesc = setup_machine_tags(machine_arch_type);
        9. machine_desc = mdesc;
        10. machine_name = mdesc->name;
        11. if (mdesc->soft_reboot)
        12. reboot_setup("s");
        13. init_mm.start_code = (unsigned long) _text;
        14. init_mm.end_code = (unsigned long) _etext;
        15. init_mm.end_data = (unsigned long) _edata;
        16. init_mm.brk = (unsigned long) _end;
        17. strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
        18. *cmdline_p = cmd_line;
        19. parse_early_param();
        20. sanity_check_meminfo();
        21. arm_memblock_init(&meminfo, mdesc);
        22. paging_init(mdesc);
        23. request_standard_resources(mdesc);
        24. unflatten_device_tree();
        25. #ifdef CONFIG_SMP
        26. if (is_smp())
        27. smp_init_cpus();
        28. #endif
        29. reserve_crashkernel();
        30. cpu_init();
        31. tcm_init();
        32. #ifdef CONFIG_MULTI_IRQ_HANDLER
        33. handle_arch_irq = mdesc->handle_irq;
        34. #endif
        35. #ifdef CONFIG_VT
        36. #if defined(CONFIG_VGA_CONSOLE)
        37. conswitchp = &vga_con;
        38. #elif defined(CONFIG_DUMMY_CONSOLE)
        39. conswitchp = &dummy_con;
        40. #endif
        41. #endif
        42. early_trap_init();
        43. if (mdesc->init_early)
        44. mdesc->init_early();
        45. }
        在上面的注釋中,我已經表明了重點和解析,下面我細化下:
        (1)獲取參數部分
        通過parse_early_param();函數可以解析內核啟動參數中的許多字符串,但是對于我們這次分析內存的話主要是分析以下兩個參數:
        mem=size@start參數,她為初始化struct meminfo meminfo;(我們一直關注的內存信息哦~)提供信息。具體的獲取信息的函數(同樣位于setup.c (archarmkernel)):
        1. int __init arm_add_memory(phys_addr_t start, unsigned long size)
        2. {
        3. struct membank *bank = &meminfo.bank[meminfo.nr_banks];
        4. if (meminfo.nr_banks >= NR_BANKS) {
        5. printk(KERN_CRIT "NR_BANKS too low, "
        6. "ignoring memory at 0xllxn", (long long)start);
        7. return -EINVAL;
        8. }
        9. /*
        10. * Ensure that start/size are aligned to a page boundary.
        11. * Size is appropriately rounded down, start is rounded up.
        12. */
        13. size -= start & ~PAGE_MASK;
        14. bank->start = PAGE_ALIGN(start);
        15. bank->size = size & PAGE_MASK;
        16. /*
        17. * Check whether this memory region has non-zero size or
        18. * invalid node number.
        19. */
        20. if (bank->size == 0)
        21. return -EINVAL;
        22. meminfo.nr_banks++;
        23. return 0;
        24. }
        25. /*
        26. * Pick out the memory size. We look for mem=size@start,
        27. * where start and size are "size[KkMm]"
        28. */
        29. static int __init early_mem(char *p)
        30. {
        31. static int usermem __initdata = 0;
        32. unsigned long size;
        33. phys_addr_t start;
        34. char *endp;
        35. /*
        36. * If the user specifies memory size, we
        37. * blow away any automatically generated
        38. * size.
        39. */
        40. if (usermem == 0) {
        41. usermem = 1;
        42. meminfo.nr_banks = 0;
        43. }
        44. start = PHYS_OFFSET;
        45. size = memparse(p, &endp);
        46. if (*endp == @)
        47. start = memparse(endp + 1, NULL);
        48. arm_add_memory(start, size);
        49. return 0;
        50. }
        51. early_param("mem", early_mem);
        vmalloc=size參數,她為初始化vmalloc_min(需要保留的內核虛擬地址空間大小,也就是這個內核虛擬地址空間中除去邏輯地址空間和必要的防止越界的保護空洞后最少要預留的地址空間)提供信息。具體的實現函數(位于mmu.c (archarmmm)):
        1. static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M);
        1. /*
        2. * vmalloc=size forces the vmalloc area to be exactly size
        3. * bytes. This can be used to increase (or decrease) the vmalloc
        4. * area - the default is 128m.
        5. */
        6. static int __init early_vmalloc(char *arg)
        7. {
        8. unsigned long vmalloc_reserve = memparse(arg, NULL);
        9. if (vmalloc_reserve < SZ_16M) {
        10. vmalloc_reserve = SZ_16M;
        11. printk(KERN_WARNING
        12. "vmalloc area too small, limiting to %luMBn",
        13. vmalloc_reserve >> 20);
        14. }
        15. if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) {
        16. vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M);
        17. printk(KERN_WARNING
        18. "vmalloc area is too big, limiting to %luMBn",
        19. vmalloc_reserve >> 20);
        20. }
        21. vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve);
        22. return 0;
        23. }
        24. early_param("vmalloc", early_vmalloc);
        (2)在獲得了必要的信息(初始化好struct meminfo meminfo和vmalloc_min)后,內核通過sanity_check_meminfo函數自動去通過vmalloc_min信息來初始化每個meminfo.bank[?]中的highmem成員。此過程中如果有必要,將可能會改變meminfo中的bank數組。處理函數位于mmu.c (archarmmm):
        1. static phys_addr_t lowmem_limit __initdata = 0;
        2. void __init sanity_check_meminfo(void)
        3. {
        4. int i, j, highmem = 0;
        5. for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
        6. struct membank *bank = &meminfo.bank[j];
        7. *bank = meminfo.bank[i];
        8. #ifdef CONFIG_HIGHMEM
        9. if (__va(bank->start) >= vmalloc_min ||
        10. __va(bank->start) < (void *)PAGE_OFFSET)
        11. highmem = 1;
        12. bank->highmem = highmem;
        13. /*
        14. * Split those memory banks which are partially overlapping
        15. * the vmalloc area greatly simplifying things later.
        16. */
        17. if (__va(bank->start) < vmalloc_min &&
        18. bank->size > vmalloc_min - __va(bank->start)) {
        19. if (meminfo.nr_banks >= NR_BANKS) {
        20. printk(KERN_CRIT "NR_BANKS too low, "
        21. "ignoring high memoryn");
        22. } else {
        23. memmove(bank + 1, bank,
        24. (meminfo.nr_banks - i) * sizeof(*bank));
        25. meminfo.nr_banks++;
        26. i++;
        27. bank[1].size -= vmalloc_min - __va(bank->start);
        28. bank[1].start = __pa(vmalloc_min - 1) + 1;
        29. bank[1].highmem = highmem = 1;
        30. j++;
        31. }
        32. bank->size = vmalloc_min - __va(bank->start);
        33. }
        34. #else
        35. bank->highmem = highmem;
        36. /*
        37. * Check whether this memory bank would entirely overlap
        38. * the vmalloc area.
        39. */
        40. if (__va(bank->start) >= vmalloc_min ||
        41. __va(bank->start) < (void *)PAGE_OFFSET) {
        42. printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "
        43. "(vmalloc region overlap).n",
        44. (unsigned long long)bank->start,
        45. (unsigned long long)bank->start + bank->size - 1);
        46. continue;
        47. }
        48. /*
        49. * Check whether this memory bank would partially overlap
        50. * the vmalloc area.
        51. */
        52. if (__va(bank->start + bank->size) > vmalloc_min ||
        53. __va(bank->start + bank->size) < __va(bank->start)) {
        54. unsigned long newsize = vmalloc_min - __va(bank->start);
        55. printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
        56. "to -%.8llx (vmalloc region overlap).n",
        57. (unsigned long long)bank->start,
        58. (unsigned long long)bank->start + bank->size - 1,
        59. (unsigned long long)bank->start + newsize - 1);
        60. bank->size = newsize;
        61. }
        62. #endif
        63. if (!bank->highmem && bank->start + bank->size > lowmem_limit)
        64. lowmem_limit = bank->start + bank->size;
        65. j++;
        66. }
        67. #ifdef CONFIG_HIGHMEM
        68. if (highmem) {
        69. const char *reason = NULL;
        70. if (cache_is_vipt_aliasing()) {
        71. /*
        72. * Interactions between kmap and other mappings
        73. * make highmem support with aliasing VIPT caches
        74. * rather difficult.
        75. */
        76. reason = "with VIPT aliasing cache";
        77. }
        78. if (reason) {
        79. printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memoryn",
        80. reason);
        81. while (j > 0 && meminfo.bank[j - 1].highmem)
        82. j--;
        83. }
        84. }
        85. #endif
        86. meminfo.nr_banks = j;
        87. memblock_set_current_limit(lowmem_limit);
        88. }
        (3)最后必須做的就是排序了,完成了這個工作就可以完全被我們上面提到的find_limits函數使用了,而這個工作就放在了接下來的arm_memblock_init(&meminfo, mdesc);中的一開頭:
        1. static int __init meminfo_cmp(const void *_a, const void *_b)
        2. {
        3. const struct membank *a = _a, *b = _b;
        4. long cmp = bank_pfn_start(a) - bank_pfn_start(b);
        5. return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
        6. }
        7. void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
        8. {
        9. int i;
        10. sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
        11. memblock_init();
        12. for (i = 0; i < mi->nr_banks; i++)
        13. memblock_add(mi->bank[i].start, mi->bank[i].size);
        14. /* Register the kernel text, kernel data and initrd with memblock. */
        15. #ifdef CONFIG_XIP_KERNEL
        16. memblock_reserve(__pa(_sdata), _end - _sdata);
        17. #else
        18. memblock_reserve(__pa(_stext), _end - _stext);
        19. #endif
        20. #ifdef CONFIG_BLK_DEV_INITRD
        21. if (phys_initrd_size &&
        22. !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) {
        23. pr_err("INITRD: 0xlx+0xlx is not a memory region - disabling initrdn",
        24. phys_initrd_start, phys_initrd_size);
        25. phys_initrd_start = phys_initrd_size = 0;
        26. }
        27. if (phys_initrd_size &&
        28. memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) {
        29. pr_err("INITRD: 0xlx+0xlx overlaps in-use memory region - disabling initrdn",
        30. phys_initrd_start, phys_initrd_size);
        31. phys_initrd_start = phys_initrd_size = 0;
        32. }
        33. if (phys_initrd_size) {
        34. memblock_reserve(phys_initrd_start, phys_initrd_size);
        35. /* Now convert initrd to virtual addresses */
        36. initrd_start = __phys_to_virt(phys_initrd_start);
        37. initrd_end = initrd_start + phys_initrd_size;
        38. }
        39. #endif
        40. arm_mm_memblock_reserve();
        41. arm_dt_memblock_reserve();
        42. /* reserve any platform specific memblock areas */
        43. if (mdesc->reserve)
        44. mdesc->reserve();
        45. memblock_analyze();
        46. memblock_dump_all();
        47. }
        通過上面的分析,整個高低端內存是如何確定的基本就清晰了,這里總結一下:
        ARM構架中,高-低段內存是內核通過內核啟動參數(mem=size@start和vmalloc=size)來自動配置的,如果沒有特殊去配置他,那么在普通的ARM系統中是不會有高端內存存在的。除非你系統的RAM很大或vmalloc配置得很大,就很可能出現高端內存。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 虹口区| 饶阳县| 易门县| 布尔津县| 石景山区| 福州市| 库伦旗| 滦南县| 阳城县| 丹棱县| 阿鲁科尔沁旗| 汝阳县| 宁海县| 达州市| 萝北县| 宜丰县| 武冈市| 大方县| 卓尼县| 金山区| 胶南市| 淳安县| 开化县| 鸡西市| 鞍山市| 肇源县| 黔南| 永胜县| 青铜峡市| 荔波县| 南漳县| 盘山县| 青海省| 海兴县| 石渠县| 乾安县| 谷城县| 怀柔区| 溆浦县| 哈密市| 潞城市|