提交 cf8fa920 编写于 作者: H H. Peter Anvin 提交者: Ingo Molnar

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: NH. Peter Anvin <hpa@zytor.com>
Signed-off-by: NThomas Gleixner <tglx@linutronix.de>
Signed-off-by: NIngo Molnar <mingo@elte.hu>
上级 6d7d7433
......@@ -195,10 +195,13 @@ cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
# can be located anywhere in
# low memory 0x10000 or higher.
ramdisk_max: .long (-__PAGE_OFFSET-(512 << 20)-1) & 0x7fffffff
ramdisk_max: .long 0x7fffffff
# (Header version 0x0203 or later)
# The highest safe address for
# the contents of an initrd
# The current kernel allows up to 4 GB,
# but leave it at 2 GB to avoid
# possible bootloader bugs.
kernel_alignment: .long CONFIG_PHYSICAL_ALIGN #physical addr alignment
#required for protected mode
......
......@@ -496,6 +496,100 @@ static inline void __init reserve_crashkernel(void)
{}
#endif
#ifdef CONFIG_BLK_DEV_INITRD
static bool do_relocate_initrd = false;
static void __init reserve_initrd(void)
{
unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
unsigned long ramdisk_end = ramdisk_image + ramdisk_size;
unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
unsigned long ramdisk_here;
initrd_start = 0;
if (!boot_params.hdr.type_of_loader ||
!ramdisk_image || !ramdisk_size)
return; /* No initrd provided by bootloader */
if (ramdisk_end < ramdisk_image) {
printk(KERN_ERR "initrd wraps around end of memory, "
"disabling initrd\n");
return;
}
if (ramdisk_size >= end_of_lowmem/2) {
printk(KERN_ERR "initrd too large to handle, "
"disabling initrd\n");
return;
}
if (ramdisk_end <= end_of_lowmem) {
/* All in lowmem, easy case */
reserve_bootmem(ramdisk_image, ramdisk_size);
initrd_start = ramdisk_image + PAGE_OFFSET;
initrd_end = initrd_start+ramdisk_size;
return;
}
/* We need to move the initrd down into lowmem */
ramdisk_here = (end_of_lowmem - ramdisk_size) & PAGE_MASK;
/* Note: this includes all the lowmem currently occupied by
the initrd, we rely on that fact to keep the data intact. */
reserve_bootmem(ramdisk_here, ramdisk_size);
initrd_start = ramdisk_here + PAGE_OFFSET;
initrd_end = initrd_start + ramdisk_size;
do_relocate_initrd = true;
}
#define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT)
static void __init relocate_initrd(void)
{
unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
unsigned long ramdisk_here;
unsigned long slop, clen, mapaddr;
char *p, *q;
if (!do_relocate_initrd)
return;
ramdisk_here = initrd_start - PAGE_OFFSET;
q = (char *)initrd_start;
/* Copy any lowmem portion of the initrd */
if (ramdisk_image < end_of_lowmem) {
clen = end_of_lowmem - ramdisk_image;
p = (char *)__va(ramdisk_image);
memcpy(q, p, clen);
q += clen;
ramdisk_image += clen;
ramdisk_size -= clen;
}
/* Copy the highmem portion of the initrd */
while (ramdisk_size) {
slop = ramdisk_image & ~PAGE_MASK;
clen = ramdisk_size;
if (clen > MAX_MAP_CHUNK-slop)
clen = MAX_MAP_CHUNK-slop;
mapaddr = ramdisk_image & PAGE_MASK;
p = bt_ioremap(mapaddr, clen+slop);
memcpy(q, p+slop, clen);
bt_iounmap(p, clen+slop);
q += clen;
ramdisk_image += clen;
ramdisk_size -= clen;
}
}
#endif /* CONFIG_BLK_DEV_INITRD */
void __init setup_bootmem_allocator(void)
{
unsigned long bootmap_size;
......@@ -551,26 +645,10 @@ void __init setup_bootmem_allocator(void)
*/
find_smp_config();
#endif
numa_kva_reserve();
#ifdef CONFIG_BLK_DEV_INITRD
if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) {
unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
unsigned long ramdisk_end = ramdisk_image + ramdisk_size;
unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
if (ramdisk_end <= end_of_lowmem) {
reserve_bootmem(ramdisk_image, ramdisk_size);
initrd_start = ramdisk_image + PAGE_OFFSET;
initrd_end = initrd_start+ramdisk_size;
} else {
printk(KERN_ERR "initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\ndisabling initrd\n",
ramdisk_end, end_of_lowmem);
initrd_start = 0;
}
}
reserve_initrd();
#endif
numa_kva_reserve();
reserve_crashkernel();
}
......@@ -713,11 +791,15 @@ void __init setup_arch(char **cmdline_p)
* NOTE: at this point the bootmem allocator is fully available.
*/
#ifdef CONFIG_BLK_DEV_INITRD
relocate_initrd();
#endif
paravirt_post_allocator_init();
dmi_scan_machine();
io_delay_init();;
io_delay_init();
#ifdef CONFIG_X86_GENERICARCH
generic_apic_probe();
......
......@@ -288,8 +288,8 @@ unsigned long __init setup_memory(void)
#ifdef CONFIG_BLK_DEV_INITRD
/* Numa kva area is below the initrd */
if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image)
kva_start_pfn = PFN_DOWN(boot_params.hdr.ramdisk_image)
if (initrd_start)
kva_start_pfn = PFN_DOWN(initrd_start - PAGE_OFFSET)
- kva_pages;
#endif
kva_start_pfn -= kva_start_pfn & (PTRS_PER_PTE-1);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册