[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