linux核心學習(4)建立正式核心的頁式記憶體對映, 以x86 32位模式為例

天麓發表於2021-01-01

linux核心學習(4)建立正式核心的頁式記憶體對映, 以x86 32位模式為例

void __init setup_arch(char **cmdline_p)
{
	max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
	=>unsigned long __init_refok init_memory_mapping(unsigned long start, unsigned long end)
	{
		for (i = 0; i < nr_range; i++)
			ret = kernel_physical_mapping_init(mr[i].start, mr[i].end, mr[i].page_size_mask);
			=>unsigned long __init kernel_physical_mapping_init(unsigned long start,
					unsigned long end,
					unsigned long page_size_mask)
			{
				和頁表項
				start_pfn = start >> PAGE_SHIFT;
				end_pfn = end >> PAGE_SHIFT;
				pgd_t *pgd_base = swapper_pg_dir;
				pages_2m = pages_4k = 0;
				pfn = start_pfn;
				pgd_idx = pgd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET);
				pgd = pgd_base + pgd_idx;
				for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {	// 第一層迴圈
					pmd = one_md_table_init(pgd); // 建立頁目錄項
					for (; pmd_idx < PTRS_PER_PMD && pfn < end_pfn; pmd++, pmd_idx++) {	// 第二層迴圈, 但是二級頁表只會迴圈一次
						unsigned int addr = pfn * PAGE_SIZE + PAGE_OFFSET;
						if (use_pse) {	// 大頁的處理
							unsigned int addr2;
							pgprot_t prot = PAGE_KERNEL_LARGE;
							/*
							 * first pass will use the same initial
							 * identity mapping attribute + _PAGE_PSE.
							 */
							pgprot_t init_prot =
								__pgprot(PTE_IDENT_ATTR |
									 _PAGE_PSE);

							addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE +
								PAGE_OFFSET + PAGE_SIZE-1;

							if (is_kernel_text(addr) ||
								is_kernel_text(addr2))
								prot = PAGE_KERNEL_LARGE_EXEC;

							pages_2m++;
							if (mapping_iter == 1)
								set_pmd(pmd, pfn_pmd(pfn, init_prot));	// 大頁只有一級頁表
							else
								set_pmd(pmd, pfn_pmd(pfn, prot));

							pfn += PTRS_PER_PTE;
							continue;
						}
						pte = one_page_table_init(pmd);
						=>pte_t * __init one_page_table_init(pmd_t *pmd)
						{
							if (!(pmd_val(*pmd) & _PAGE_PRESENT)) {	// 生成頁表
								pte_t *page_table = NULL;

								if (after_bootmem) {
						#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK)
									page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
						#endif
									if (!page_table)
										page_table =
										(pte_t *)alloc_bootmem_pages(PAGE_SIZE);
								} else
									page_table = (pte_t *)alloc_low_page();

								paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT);
								set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); // 將頁表實體地址寫在頁目錄項
								BUG_ON(page_table != pte_offset_kernel(pmd, 0));
							}

							return pte_offset_kernel(pmd, 0);
						}

						pte_ofs = pte_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET);
						pte += pte_ofs;
						for (; pte_ofs < PTRS_PER_PTE && pfn < end_pfn; pte++, pfn++, pte_ofs++, addr += PAGE_SIZE) {	// 第三層迴圈
							pgprot_t prot = PAGE_KERNEL;
							/*
							 * first pass will use the same initial
							 * identity mapping attribute.
							 */
							pgprot_t init_prot = __pgprot(PTE_IDENT_ATTR);

							if (is_kernel_text(addr))
								prot = PAGE_KERNEL_EXEC;

							pages_4k++;
							if (mapping_iter == 1) {	// 將頁實體地址填充到頁表項
								set_pte(pte, pfn_pte(pfn, init_prot));
								last_map_addr = (pfn << PAGE_SHIFT) + PAGE_SIZE;
							} else
								set_pte(pte, pfn_pte(pfn, prot));
						}
					}
				}
				
			}
		early_ioremap_page_table_range_init();

		load_cr3(swapper_pg_dir);
	}
}

Linux核心原始碼學習 (1)- 從真實模式到保護模式
https://my.oschina.net/u/135465/blog/99590

相關文章