[parisc-linux-cvs] patch for initrd, mem= interaction

Bjorn Helgaas bjorn_helgaas@hp.com
Sat, 14 Apr 2001 16:40:35 -0600


Here's a patch for initrd handling.  Comments & suggestions requested!

Palo loads ramdisks up high in physical memory.  If you artificially 
limited the memory size by passing a "mem=XX" argument, it's likely that 
the ramdisk will be above the new memory size.  The PA kernel previously 
didn't check for that and blindly called reserve_bootmem_node() on the 
ramdisk, which interpreted some random memory as the bootmem bitmap.  Most 
other architectures seem to just ignore an initrd in this case, but I want 
to be able to use an initrd with "mem=XX" because it's a lot faster to 
simulate a 32M machine than a 2G one.

This patch only reserves the part of the ramdisk that is below mem_max, 
i.e., the part that the bootmem allocator knows about.

It also maps the ramdisk, since it now may be outside the usual mapping of 
"all the memory known by the kernel."  This may result in it being mapped 
twice, but both mappings are the same.

Since the mapping code is now used in three places (pagetable_init() for 
the usual mapping of all physical memory ranges, gateway_init(), and the 
new ramdisk mapping (also in pagetable_init()), I pulled it out into its 
own function, called map_pages().  Apart from the obvious 
parameterization, the only changes from the old place in pagetable_init() 
are the

	start_pte = ((start_vaddr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
and
	pg_table = (pte_t *) __va(pg_table) + start_pte;

lines; these find the pg_table entry correctly even when the beginning of 
the virtual range is not aligned to be the first entry in a pg_table.

Bjorn



Index: arch/parisc/mm/init.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/mm/init.c,v
retrieving revision 1.32
diff -u -p -r1.32 init.c
--- init.c	2001/03/22 16:24:18	1.32
+++ init.c	2001/04/13 23:24:54
@@ -334,11 +334,21 @@ static void __init setup_bootmem(void)
 #endif
 
 #ifdef CONFIG_BLK_DEV_INITRD
-	printk("initrd: %08x-%08x\n", (int) initrd_start, (int) initrd_end);
+	printk("initrd: %08lx-%08lx\n", initrd_start, initrd_end);
 
 	if (initrd_end != 0) {
-		initrd_below_start_ok = 1;
-		reserve_bootmem_node(NODE_DATA(0),__pa(initrd_start), initrd_end - 
initrd_start);
+		if (__pa(initrd_start) < mem_max) {
+			unsigned long initrd_reserve;
+
+			if (__pa(initrd_end) > mem_max) {
+				initrd_reserve = mem_max - __pa(initrd_start);
+			} else {
+				initrd_reserve = initrd_end - initrd_start;
+			}
+			initrd_below_start_ok = 1;
+			printk("initrd: reserving %08lx-%08lx (mem_max %08lx)\n", 
__pa(initrd_start), __pa(initrd_start) + initrd_reserve, mem_max);
+			reserve_bootmem_node(NODE_DATA(0), __pa(initrd_start), initrd_reserve);
+		}
 	}
 #endif
 
@@ -497,98 +507,86 @@ void set_pte_phys (unsigned long vaddr, 
 {
 }
 
-/*
- * pagetable_init() sets up the page tables
- *
- * Note that gateway_init() places the Linux gateway page at page 0.
- * Since gateway pages cannot be dereferenced this has the desirable
- * side effect of trapping those pesky NULL-reference errors in the
- * kernel.
- */
-static void __init pagetable_init(void)
+static void __init map_pages(unsigned long start_vaddr, unsigned long 
start_paddr, unsigned long size, pgprot_t pgprot)
 {
 	pgd_t *pg_dir;
 	pmd_t *pmd;
 	pte_t *pg_table;
+	unsigned long end_paddr;
 	unsigned long start_pmd;
+	unsigned long start_pte;
 	unsigned long tmp1;
 	unsigned long tmp2;
 	unsigned long address;
 	unsigned long ro_start;
 	unsigned long ro_end;
 	unsigned long fv_addr;
-	int range;
 	extern  const int stext;
 	extern  int data_start;
 	extern  const unsigned long fault_vector_20;
 
+	printk("map_pages: 0x%08lx -> 0x%08lx, size 0x%lx\n",
+		start_vaddr, start_paddr, size);
+
 	ro_start = __pa((unsigned long)&stext);
 	ro_end   = __pa((unsigned long)&data_start);
 	fv_addr  = __pa((unsigned long)&fault_vector_20) & PAGE_MASK;
-
-	printk("pagetable_init\n");
 
-	/* Map each physical memory range to its kernel vaddr */
+	end_paddr = start_paddr + size;
 
-	for (range = 0; range < npmem_ranges; range++) {
-		unsigned long start_paddr;
-		unsigned long end_paddr;
+	pg_dir = pgd_offset_k(start_vaddr);
 
-		start_paddr = pmem_ranges[range].start_pfn << PAGE_SHIFT;
-		end_paddr = start_paddr + (pmem_ranges[range].pages << PAGE_SHIFT);
-
-		pg_dir = pgd_offset_k(start_paddr + PAGE_OFFSET);
-
 #if PTRS_PER_PMD == 1
-		start_pmd = 0;
+	start_pmd = 0;
 #else
-		start_pmd = (((start_paddr + PAGE_OFFSET) >> PMD_SHIFT) & (PTRS_PER_PMD 
- 1));
+	start_pmd = ((start_vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1));
 #endif
+	start_pte = ((start_vaddr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
 
-		address = start_paddr;
-		while (address < end_paddr) {
+	address = start_paddr;
+	while (address < end_paddr) {
 #if PTRS_PER_PMD == 1
-			pmd = (pmd_t *)__pa(pg_dir);
+		pmd = (pmd_t *)__pa(pg_dir);
 #else
-			pmd = (pmd_t *) (PAGE_MASK & pgd_val(*pg_dir));
+		pmd = (pmd_t *) (PAGE_MASK & pgd_val(*pg_dir));
 
-			/*
-			 * pmd is physical at this point
-			 */
-
-			if (!pmd) {
-				pmd = (pmd_t *) alloc_bootmem_low_pages_node(NODE_DATA(0),PAGE_SIZE);
-				pmd = (pmd_t *) __pa(pmd);
-			}
+		/*
+		 * pmd is physical at this point
+		 */
+
+		if (!pmd) {
+			pmd = (pmd_t *) alloc_bootmem_low_pages_node(NODE_DATA(0),PAGE_SIZE);
+			pmd = (pmd_t *) __pa(pmd);
+		}
 
-			pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pmd;
+		pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pmd;
 #endif
-			pg_dir++;
+		pg_dir++;
 
-			/* now change pmd to kernel virtual addresses */
+		/* now change pmd to kernel virtual addresses */
 
-			pmd = (pmd_t *)__va(pmd) + start_pmd;
-			for (tmp1 = start_pmd; tmp1 < PTRS_PER_PMD; tmp1++,pmd++) {
+		pmd = (pmd_t *)__va(pmd) + start_pmd;
+		for (tmp1 = start_pmd; tmp1 < PTRS_PER_PMD; tmp1++,pmd++) {
 
-				/*
-				 * pg_table is physical at this point
-				 */
+			/*
+			 * pg_table is physical at this point
+			 */
 
-				pg_table = (pte_t *) (PAGE_MASK & pmd_val(*pmd));
-				if (!pg_table) {
-					pg_table = (pte_t *)
-						alloc_bootmem_low_pages_node(NODE_DATA(0),PAGE_SIZE);
-					pg_table = (pte_t *) __pa(pg_table);
-				}
+			pg_table = (pte_t *) (PAGE_MASK & pmd_val(*pmd));
+			if (!pg_table) {
+				pg_table = (pte_t *)
+					alloc_bootmem_low_pages_node(NODE_DATA(0),PAGE_SIZE);
+				pg_table = (pte_t *) __pa(pg_table);
+			}
 
-				pmd_val(*pmd) = _PAGE_TABLE |
-						   (unsigned long) pg_table;
+			pmd_val(*pmd) = _PAGE_TABLE |
+					   (unsigned long) pg_table;
 
-				/* now change pg_table to kernel virtual addresses */
+			/* now change pg_table to kernel virtual addresses */
 
-				pg_table = (pte_t *) __va(pg_table);
-				for (tmp2=0; tmp2 < PTRS_PER_PTE; tmp2++,pg_table++) {
-					pte_t pte;
+			pg_table = (pte_t *) __va(pg_table) + start_pte;
+			for (tmp2 = start_pte; tmp2 < PTRS_PER_PTE; tmp2++,pg_table++) {
+				pte_t pte;
 
 #if !defined(CONFIG_KWDB) && !defined(CONFIG_STI_CONSOLE)
 #warning STI console should explicitly allocate executable pages but does 
not
@@ -597,28 +595,65 @@ static void __init pagetable_init(void)
 ** The right thing to do seems like KWDB modify only the pte which
 ** has a break point on it...otherwise we might mask worse bugs.
 */
-					if (address >= ro_start && address < ro_end
-								&& address != fv_addr)
-					    pte = __mk_pte(address, PAGE_KERNEL_RO);
-					else
+				if (address >= ro_start && address < ro_end
+							&& address != fv_addr)
+				    pte = __mk_pte(address, PAGE_KERNEL_RO);
+				else
 #endif
-					    pte = __mk_pte(address, PAGE_KERNEL);
+				    pte = __mk_pte(address, pgprot);
 
-					if (address >= end_paddr)
-						pte_val(pte) = 0;
-
-					set_pte(pg_table, pte);
+				if (address >= end_paddr)
+					pte_val(pte) = 0;
 
-					address += PAGE_SIZE;
-				}
+				set_pte(pg_table, pte);
 
-				if (address >= end_paddr)
-				    break;
+				address += PAGE_SIZE;
 			}
-			start_pmd = 0;
+			start_pte = 0;
+
+			if (address >= end_paddr)
+			    break;
 		}
+		start_pmd = 0;
+	}
+}
+
+/*
+ * pagetable_init() sets up the page tables
+ *
+ * Note that gateway_init() places the Linux gateway page at page 0.
+ * Since gateway pages cannot be dereferenced this has the desirable
+ * side effect of trapping those pesky NULL-reference errors in the
+ * kernel.
+ */
+static void __init pagetable_init(void)
+{
+	int range;
+
+	printk("pagetable_init\n");
+
+	/* Map each physical memory range to its kernel vaddr */
+
+	for (range = 0; range < npmem_ranges; range++) {
+		unsigned long start_paddr;
+		unsigned long end_paddr;
+		unsigned long size;
+
+		start_paddr = pmem_ranges[range].start_pfn << PAGE_SHIFT;
+		end_paddr = start_paddr + (pmem_ranges[range].pages << PAGE_SHIFT);
+		size = pmem_ranges[range].pages << PAGE_SHIFT;
+
+		map_pages(__va(start_paddr), start_paddr, size, PAGE_KERNEL);
 	}
 
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (initrd_end && initrd_end > mem_limit) {
+		printk("initrd: mapping %08lx-%08lx\n", initrd_start, initrd_end);
+		map_pages(initrd_start, __pa(initrd_start),
+			initrd_end - initrd_start, PAGE_KERNEL);
+	}
+#endif
+
 	empty_zero_page = alloc_bootmem_pages(PAGE_SIZE);
 	memset(empty_zero_page, 0, PAGE_SIZE);
 }
@@ -626,15 +661,9 @@ static void __init pagetable_init(void)
 static void __init gateway_init(void)
 {
 	unsigned long linux_gateway_page_addr;
-	pgd_t *pg_dir;
-	pmd_t *pmd_base;
-	pmd_t *pmd;
-	pte_t *pg_table_base;
-	pte_t *pg_table;
 	/* FIXME: This is 'const' in order to trick the compiler
 	   into not treating it as DP-relative data. */
 	extern void * const linux_gateway_page;
-	pte_t pte;
 
 	linux_gateway_page_addr = LINUX_GATEWAY_ADDR & PAGE_MASK;
 
@@ -644,27 +673,9 @@ static void __init gateway_init(void)
 	 * The Linux gateway page will reside in kernel space (on virtual
 	 * page 0), so it doesn't need to be aliased into user space.
 	 */
-
-	pg_dir = pgd_offset_k(linux_gateway_page_addr);
-
-#if (PTRS_PER_PMD != 1)
-	if (pgd_none(*pg_dir)) {
-		pmd_base = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE);
-		pgd_val(*pg_dir) = _PAGE_TABLE | __pa(pmd_base);
-	}
-#endif
-
-	pmd = pmd_offset(pg_dir,linux_gateway_page_addr);
-	if (pmd_none(*pmd)) {
-		pg_table_base = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
-		pmd_val(*pmd) = _PAGE_TABLE | __pa(pg_table_base);
-	}
-
-	pg_table = pte_offset(pmd,linux_gateway_page_addr);
-	pte = __mk_pte(__pa(&linux_gateway_page), PAGE_GATEWAY);
-	set_pte(pg_table,pte);
 
-	return;
+	map_pages(linux_gateway_page_addr, __pa(&linux_gateway_page),
+		PAGE_SIZE, PAGE_GATEWAY);
 }
 
 void __init paging_init(void)