[gPXE-devel] [PATCH][lkrn] add cmdline and ramdisk support
Wu Fengguang
fengguang.wu at intel.com
Mon May 31 03:54:50 EDT 2010
Allow passing command line parameters to gpxe.lkrn. For example,
pxelinux.cfg: (just a demo, we already have gpxelinux)
LABEL gpxe
KERNEL gpxe.lkrn
APPEND kernel http://somewhere/~wfg/cgi-bin/gpxe.cgi?mac=${mac}
/boot/grub/grub.cfg: (this use case should be valuable)
menuentry "gPXE" {
echo Loading gPXE ...
linux16 /boot/gpxe.lkrn kernel http://somewhere/rip/kernel64 root=/dev/ram0 \
initrd http://somewhere/rip/rootfs.cgz
}
The command line syntax is "kernel URL [initrd URL]". One may add ';'
to truncate extra parameters. For example:
gpxe cmdline linux /proc/cmdline
-------------------------------------------------------------------
kernel URL root=/dev/ram0 initrd URL root=/dev/ram0 initrd URL
kernel URL; root=/dev/ram0 initrd URL root=/dev/ram0
Note that the auto-added BOOT_IMAGE=xxx may be dropped by the simple
cmdline parsing.
Also allow passing ramdisk script to gpxe: (we can already _embed_ the
script into gpxe.lkrn, however it's much more convenient for end user)
/boot/grub/grub.cfg:
menuentry "gPXE" {
echo Loading gPXE ...
linux16 /boot/gpxe.lkrn
initrd16 /boot/bee.gpxe
}
/boot/bee.gpxe:
#!gpxe
dhcp net0
kernel http://somewhere/~wfg/cgi-bin/gpxe.cgi?mac=${net0/mac}
boot
The ramdisk memory is hidden to avoid it being overwritten.
The cmdline memory is unattended, otherwise it will break meme820()'s
assumption that the _second_ e820 memory region starts at 0x100000.
And I guess it's safe to leave it unattended.
Tested OK with GRUB2, PXELinux and KVM.
Signed-off-by: Wu Fengguang <fengguang.wu at intel.com>
---
src/arch/i386/core/relocate.c | 23 +++++++--
src/arch/i386/firmware/pcbios/e820mangler.S | 4 +
src/arch/i386/firmware/pcbios/hidemem.c | 12 +++++
src/arch/i386/prefix/libprefix.S | 7 +++
src/arch/i386/prefix/lkrnprefix.S | 5 +-
src/image/embedded.c | 19 ++++++++
src/include/gpxe/hidemem.h | 1
src/include/gpxe/image.h | 11 ++++
src/usr/autoboot.c | 42 ++++++++++++++++++
9 files changed, 118 insertions(+), 6 deletions(-)
--- gpxe.orig/src/arch/i386/prefix/lkrnprefix.S 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/arch/i386/prefix/lkrnprefix.S 2010-05-31 15:40:18.000000000 +0800
@@ -134,8 +134,10 @@ setup_move_size:
.word 0
code32_start:
.long 0
+.globl ramdisk_image
ramdisk_image:
.long 0
+.globl ramdisk_size
ramdisk_size:
.long 0
bootsect_kludge:
@@ -144,6 +146,7 @@ heap_end_ptr:
.word 0
pad1:
.word 0
+.globl cmd_line_ptr
cmd_line_ptr:
.long 0
initrd_addr_max:
@@ -158,7 +161,7 @@ relocatable_kernel:
pad2:
.byte 0, 0, 0
cmdline_size:
- .long 0
+ .long 255
hardware_subarch:
.long 0
hardware_subarch_data:
--- gpxe.orig/src/image/embedded.c 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/image/embedded.c 2010-05-31 15:40:18.000000000 +0800
@@ -50,6 +50,15 @@ static struct image embedded_images[] =
EMBED_ALL
};
+unsigned long __data16 ( linux_cmdline ) = 0;
+unsigned long __data16 ( linux_ramdisk ) = 0;
+unsigned long __data16 ( linux_ramdisk_size ) = 0;
+
+static struct image preloaded_image = {
+ .refcnt = { .free = embedded_image_free, },
+ .name = "initrd",
+};
+
/**
* Register all embedded images
*/
@@ -59,6 +68,16 @@ static void embedded_init ( void ) {
void *data;
int rc;
+ if (linux_ramdisk && linux_ramdisk_size) {
+ DBG ("Found ramdisk at %lx size %ld\n",
+ linux_ramdisk, linux_ramdisk_size);
+ preloaded_image.data = phys_to_user( linux_ramdisk );
+ preloaded_image.len = linux_ramdisk_size;
+ if ( ! register_image ( &preloaded_image ) &&
+ ! image_autoload ( &preloaded_image ) )
+ return;
+ }
+
/* Skip if we have no embedded images */
if ( ! sizeof ( embedded_images ) )
return;
--- gpxe.orig/src/include/gpxe/image.h 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/include/gpxe/image.h 2010-05-31 15:40:18.000000000 +0800
@@ -191,4 +191,15 @@ static inline int image_set_name ( struc
return 0;
}
+extern char preloaded_ramdisk[128];
+extern char *userimage_addr;
+extern unsigned long userimage_size;
+
+extern unsigned long __data16 ( linux_cmdline );
+extern unsigned long __data16 ( linux_ramdisk );
+extern unsigned long __data16 ( linux_ramdisk_size );
+#define linux_cmdline __use_data16 ( linux_cmdline )
+#define linux_ramdisk __use_data16 ( linux_ramdisk )
+#define linux_ramdisk_size __use_data16 ( linux_ramdisk_size )
+
#endif /* _GPXE_IMAGE_H */
--- gpxe.orig/src/arch/i386/prefix/libprefix.S 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/arch/i386/prefix/libprefix.S 2010-05-31 15:40:18.000000000 +0800
@@ -704,6 +704,13 @@ install_prealloc:
/* Set up %ds for access to .data16 */
movw %bx, %ds
+ movl %es:cmd_line_ptr, %ecx
+ movl %ecx, linux_cmdline
+ movl %es:ramdisk_image, %ecx
+ movl %ecx, linux_ramdisk
+ movl %es:ramdisk_size, %ecx
+ movl %ecx, linux_ramdisk_size
+
#ifdef KEEP_IT_REAL
/* Initialise libkir */
movw %ax, (init_libkir_vector+2)
--- gpxe.orig/src/usr/autoboot.c 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/usr/autoboot.c 2010-05-31 15:53:30.000000000 +0800
@@ -122,6 +122,30 @@ int boot_root_path ( const char *root_pa
return -ENOTSUP;
}
+int exec_cmdline ( const char *buf, const char *prefix, int *match ) {
+ char *p, *q;
+ char cmd[256];
+
+ p = strstr ( buf, prefix );
+ if ( ! p )
+ return 0;
+
+ for ( q = cmd; *p && *p != ';'; )
+ *q++ = *p++;
+ *q = '\0';
+
+ /* truncate the PXELinux appended BOOT_IMAGE= */
+ if ( *match ) {
+ p = strstr ( cmd, "BOOT_IMAGE=" );
+ if ( p )
+ *( p - 1 ) = '\0';
+ }
+
+ ( *match )++;
+
+ return system( cmd );
+}
+
/**
* Boot from a network device
*
@@ -163,6 +187,24 @@ static int netboot ( struct net_device *
return pxe_menu_boot ( netdev );
}
+ if ( linux_cmdline ) {
+ int match = 0;
+
+ DBG ( "Found cmdline at %lx: ", linux_cmdline );
+ copy_from_user ( buf, phys_to_user( linux_cmdline ),
+ 0, sizeof ( buf ) );
+ buf[sizeof ( buf ) - 1] = '\0';
+
+ rc = exec_cmdline( buf, "kernel ", &match );
+ if ( rc )
+ return rc;
+ rc = exec_cmdline( buf, "initrd ", &match );
+ if ( rc )
+ return rc;
+ if ( match )
+ return system( "boot" );
+ }
+
/* Try to download and boot whatever we are given as a filename */
fetch_ipv4_setting ( NULL, &next_server_setting, &next_server );
fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) );
--- gpxe.orig/src/arch/i386/core/relocate.c 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/arch/i386/core/relocate.c 2010-05-31 15:40:18.000000000 +0800
@@ -1,6 +1,8 @@
#include <gpxe/io.h>
#include <registers.h>
#include <gpxe/memmap.h>
+#include <gpxe/image.h>
+#include <gpxe/hidemem.h>
/*
* Originally by Eric Biederman
@@ -46,6 +48,7 @@ __asmcall void relocate ( struct i386_al
unsigned long start, end, size, padded_size;
unsigned long new_start, new_end;
unsigned i;
+ unsigned long top = MAX_ADDR;
/* Get memory map and current location */
get_memmap ( &memmap );
@@ -58,6 +61,16 @@ __asmcall void relocate ( struct i386_al
"...need %lx bytes for %d-byte alignment\n",
start, end, padded_size, max_align );
+ if (linux_ramdisk && linux_ramdisk_size) {
+ hide_ramdisk(linux_ramdisk, linux_ramdisk + linux_ramdisk_size);
+
+ /* hide_ramdisk() takes effect in future.
+ * For now simply lower the top location to hide it.
+ */
+ if (top > linux_ramdisk)
+ top = linux_ramdisk & ~0xfffff;
+ }
+
/* Walk through the memory map and find the highest address
* below 4GB that etherboot will fit into. Ensure etherboot
* lies entirely within a range with A20=0. This means that
@@ -76,14 +89,14 @@ __asmcall void relocate ( struct i386_al
* 4GB, which means that we can get away with using
* just 32-bit arithmetic after this stage.
*/
- if ( region->start > MAX_ADDR ) {
- DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR );
+ if ( region->start > top ) {
+ DBG ( "...starts after top=%lx\n", top );
continue;
}
r_start = region->start;
- if ( region->end > MAX_ADDR ) {
- DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR );
- r_end = MAX_ADDR;
+ if ( region->end > top ) {
+ DBG ( "...end truncated to top=%lx\n", top );
+ r_end = top;
} else {
r_end = region->end;
}
--- gpxe.orig/src/arch/i386/firmware/pcbios/e820mangler.S 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/arch/i386/firmware/pcbios/e820mangler.S 2010-05-31 15:40:18.000000000 +0800
@@ -66,6 +66,7 @@ FILE_LICENCE ( GPL2_OR_LATER )
.globl hidemem_base
.globl hidemem_umalloc
.globl hidemem_textdata
+ .globl hidemem_ramdisk
memory_windows:
base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
@@ -78,6 +79,9 @@ hidemem_umalloc: .long 0xffffffff, 0xfff
hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
.long 0xffffffff, 0xffffffff /* Changes at runtime */
+hidemem_ramdisk: .long 0xffffffff, 0xffffffff /* Changes at runtime */
+ .long 0xffffffff, 0xffffffff /* Changes at runtime */
+
.long 0xffffffff, 0xffffffff /* End of memory */
memory_windows_end:
--- gpxe.orig/src/include/gpxe/hidemem.h 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/include/gpxe/hidemem.h 2010-05-31 15:40:18.000000000 +0800
@@ -13,5 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
extern void hide_umalloc ( physaddr_t start, physaddr_t end );
+extern void hide_ramdisk ( physaddr_t start, physaddr_t end );
#endif /* _GPXE_HIDEMEM_H */
--- gpxe.orig/src/arch/i386/firmware/pcbios/hidemem.c 2010-05-31 15:40:17.000000000 +0800
+++ gpxe/src/arch/i386/firmware/pcbios/hidemem.c 2010-05-31 15:40:18.000000000 +0800
@@ -60,6 +60,10 @@ extern struct hidden_region __data16 ( h
extern struct hidden_region __data16 ( hidemem_textdata );
#define hidemem_textdata __use_data16 ( hidemem_textdata )
+/** Hidden ramdisk memory */
+extern struct hidden_region __data16 ( hidemem_ramdisk );
+#define hidemem_ramdisk __use_data16 ( hidemem_ramdisk )
+
/** Assembly routine in e820mangler.S */
extern void int15();
@@ -126,6 +130,14 @@ void hide_textdata ( void ) {
}
/**
+ * Hide linux ramdisk
+ *
+ */
+void hide_ramdisk ( physaddr_t start, physaddr_t end ) {
+ hide_region ( &hidemem_ramdisk, start, end );
+}
+
+/**
* Hide Etherboot
*
* Installs an INT 15 handler to edit Etherboot out of the memory map
More information about the gPXE-devel
mailing list