aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/setup_32.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-01-30 07:32:51 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:32:51 -0500
commitcf8fa920cb4271f17e0265c863d64bea1b31941a (patch)
tree5a036d33e78108c9a762913155823ff0c91051f5 /arch/x86/kernel/setup_32.c
parent6d7d7433750c7c6eec93d7b3206019e329228686 (diff)
i386: handle an initrd in highmem (version 2)
The boot protocol has until now required that the initrd be located in lowmem, which makes the lowmem/highmem boundary visible to the boot loader. This was exported to the bootloader via a compile-time field. Unfortunately, the vmalloc= command-line option breaks this part of the protocol; instead of adding yet another hack that affects the bootloader, have the kernel relocate the initrd down below the lowmem boundary inside the kernel itself. Note that this does not rely on HIGHMEM being enabled in the kernel. Signed-off-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/setup_32.c')
-rw-r--r--arch/x86/kernel/setup_32.c130
1 files changed, 106 insertions, 24 deletions
diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c
index 3bce4af60bb6..6802a383077d 100644
--- a/arch/x86/kernel/setup_32.c
+++ b/arch/x86/kernel/setup_32.c
@@ -188,9 +188,9 @@ extern int root_mountflags;
188 188
189unsigned long saved_videomode; 189unsigned long saved_videomode;
190 190
191#define RAMDISK_IMAGE_START_MASK 0x07FF 191#define RAMDISK_IMAGE_START_MASK 0x07FF
192#define RAMDISK_PROMPT_FLAG 0x8000 192#define RAMDISK_PROMPT_FLAG 0x8000
193#define RAMDISK_LOAD_FLAG 0x4000 193#define RAMDISK_LOAD_FLAG 0x4000
194 194
195static char __initdata command_line[COMMAND_LINE_SIZE]; 195static char __initdata command_line[COMMAND_LINE_SIZE];
196 196
@@ -252,7 +252,7 @@ static int __init parse_mem(char *arg)
252 * trim the existing memory map. 252 * trim the existing memory map.
253 */ 253 */
254 unsigned long long mem_size; 254 unsigned long long mem_size;
255 255
256 mem_size = memparse(arg, &arg); 256 mem_size = memparse(arg, &arg);
257 limit_regions(mem_size); 257 limit_regions(mem_size);
258 user_defined_memmap = 1; 258 user_defined_memmap = 1;
@@ -391,7 +391,7 @@ static void __init reserve_ebda_region(void)
391 unsigned int addr; 391 unsigned int addr;
392 addr = get_bios_ebda(); 392 addr = get_bios_ebda();
393 if (addr) 393 if (addr)
394 reserve_bootmem(addr, PAGE_SIZE); 394 reserve_bootmem(addr, PAGE_SIZE);
395} 395}
396 396
397#ifndef CONFIG_NEED_MULTIPLE_NODES 397#ifndef CONFIG_NEED_MULTIPLE_NODES
@@ -496,6 +496,100 @@ static inline void __init reserve_crashkernel(void)
496{} 496{}
497#endif 497#endif
498 498
499#ifdef CONFIG_BLK_DEV_INITRD
500
501static bool do_relocate_initrd = false;
502
503static void __init reserve_initrd(void)
504{
505 unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
506 unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
507 unsigned long ramdisk_end = ramdisk_image + ramdisk_size;
508 unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
509 unsigned long ramdisk_here;
510
511 initrd_start = 0;
512
513 if (!boot_params.hdr.type_of_loader ||
514 !ramdisk_image || !ramdisk_size)
515 return; /* No initrd provided by bootloader */
516
517 if (ramdisk_end < ramdisk_image) {
518 printk(KERN_ERR "initrd wraps around end of memory, "
519 "disabling initrd\n");
520 return;
521 }
522 if (ramdisk_size >= end_of_lowmem/2) {
523 printk(KERN_ERR "initrd too large to handle, "
524 "disabling initrd\n");
525 return;
526 }
527 if (ramdisk_end <= end_of_lowmem) {
528 /* All in lowmem, easy case */
529 reserve_bootmem(ramdisk_image, ramdisk_size);
530 initrd_start = ramdisk_image + PAGE_OFFSET;
531 initrd_end = initrd_start+ramdisk_size;
532 return;
533 }
534
535 /* We need to move the initrd down into lowmem */
536 ramdisk_here = (end_of_lowmem - ramdisk_size) & PAGE_MASK;
537
538 /* Note: this includes all the lowmem currently occupied by
539 the initrd, we rely on that fact to keep the data intact. */
540 reserve_bootmem(ramdisk_here, ramdisk_size);
541 initrd_start = ramdisk_here + PAGE_OFFSET;
542 initrd_end = initrd_start + ramdisk_size;
543
544 do_relocate_initrd = true;
545}
546
547#define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT)
548
549static void __init relocate_initrd(void)
550{
551 unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
552 unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
553 unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
554 unsigned long ramdisk_here;
555 unsigned long slop, clen, mapaddr;
556 char *p, *q;
557
558 if (!do_relocate_initrd)
559 return;
560
561 ramdisk_here = initrd_start - PAGE_OFFSET;
562
563 q = (char *)initrd_start;
564
565 /* Copy any lowmem portion of the initrd */
566 if (ramdisk_image < end_of_lowmem) {
567 clen = end_of_lowmem - ramdisk_image;
568 p = (char *)__va(ramdisk_image);
569 memcpy(q, p, clen);
570 q += clen;
571 ramdisk_image += clen;
572 ramdisk_size -= clen;
573 }
574
575 /* Copy the highmem portion of the initrd */
576 while (ramdisk_size) {
577 slop = ramdisk_image & ~PAGE_MASK;
578 clen = ramdisk_size;
579 if (clen > MAX_MAP_CHUNK-slop)
580 clen = MAX_MAP_CHUNK-slop;
581 mapaddr = ramdisk_image & PAGE_MASK;
582 p = bt_ioremap(mapaddr, clen+slop);
583 memcpy(q, p+slop, clen);
584 bt_iounmap(p, clen+slop);
585 q += clen;
586 ramdisk_image += clen;
587 ramdisk_size -= clen;
588 }
589}
590
591#endif /* CONFIG_BLK_DEV_INITRD */
592
499void __init setup_bootmem_allocator(void) 593void __init setup_bootmem_allocator(void)
500{ 594{
501 unsigned long bootmap_size; 595 unsigned long bootmap_size;
@@ -551,26 +645,10 @@ void __init setup_bootmem_allocator(void)
551 */ 645 */
552 find_smp_config(); 646 find_smp_config();
553#endif 647#endif
554 numa_kva_reserve();
555#ifdef CONFIG_BLK_DEV_INITRD 648#ifdef CONFIG_BLK_DEV_INITRD
556 if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) { 649 reserve_initrd();
557 unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
558 unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
559 unsigned long ramdisk_end = ramdisk_image + ramdisk_size;
560 unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
561
562 if (ramdisk_end <= end_of_lowmem) {
563 reserve_bootmem(ramdisk_image, ramdisk_size);
564 initrd_start = ramdisk_image + PAGE_OFFSET;
565 initrd_end = initrd_start+ramdisk_size;
566 } else {
567 printk(KERN_ERR "initrd extends beyond end of memory "
568 "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
569 ramdisk_end, end_of_lowmem);
570 initrd_start = 0;
571 }
572 }
573#endif 650#endif
651 numa_kva_reserve();
574 reserve_crashkernel(); 652 reserve_crashkernel();
575} 653}
576 654
@@ -713,15 +791,19 @@ void __init setup_arch(char **cmdline_p)
713 * NOTE: at this point the bootmem allocator is fully available. 791 * NOTE: at this point the bootmem allocator is fully available.
714 */ 792 */
715 793
794#ifdef CONFIG_BLK_DEV_INITRD
795 relocate_initrd();
796#endif
797
716 paravirt_post_allocator_init(); 798 paravirt_post_allocator_init();
717 799
718 dmi_scan_machine(); 800 dmi_scan_machine();
719 801
720 io_delay_init();; 802 io_delay_init();
721 803
722#ifdef CONFIG_X86_GENERICARCH 804#ifdef CONFIG_X86_GENERICARCH
723 generic_apic_probe(); 805 generic_apic_probe();
724#endif 806#endif
725 if (efi_enabled) 807 if (efi_enabled)
726 efi_map_memmap(); 808 efi_map_memmap();
727 809