[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)