Linux-loading module with optional DHCP generation. syslinux-3.40-pre15
authorH. Peter Anvin <hpa@zytor.com>
Fri, 16 Mar 2007 01:20:55 +0000 (18:20 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 16 Mar 2007 01:20:55 +0000 (18:20 -0700)
Load a Linux module, generate initramfs and optionally add
/dhcpinfo.dat with the DHCP query information.

com32/modules/Makefile
com32/modules/linux.c [new file with mode: 0644]

index 19fb3b3..7f3eae5 100644 (file)
@@ -45,7 +45,7 @@ INCDIR   = /usr/include
 COM32DIR = $(AUXDIR)/com32
 
 MODULES          = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 \
-           dmitest.c32 cpuidtest.c32 pcitest.c32 elf.c32
+           dmitest.c32 cpuidtest.c32 pcitest.c32 elf.c32 linux.c32
 TESTFILES =
 
 all: $(MODULES) $(TESTFILES)
diff --git a/com32/modules/linux.c b/com32/modules/linux.c
new file mode 100644 (file)
index 0000000..dc5da57
--- /dev/null
@@ -0,0 +1,236 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2007 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * linux.c
+ *
+ * Sample module to load Linux kernels.  This module can also create
+ * a file out of the DHCP return data if running under PXELINUX.
+ *
+ * If -dhcpinfo is specified, the DHCP info is written into the file
+ * /dhcpinfo.dat in the initramfs.
+ *
+ * Usage: linux.c32 [-dhcpinfo] kernel arguments...
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <console.h>
+#include <syslinux/loadfile.h>
+#include <syslinux/linux.h>
+#include <syslinux/pxe.h>
+
+const char *progname = "linux.c32";
+
+/* Find the last instance of a particular command line argument
+   (which should include the final =; do not use for boolean arguments) */
+static char *find_argument(char **argv, const char *argument)
+{
+  int la = strlen(argument);
+  char **arg;
+  char *ptr = NULL;
+
+  for (arg = argv; *arg; arg++) {
+    if (!memcmp(*arg, argument, la))
+      ptr = *arg + la;
+  }
+
+  return ptr;
+}
+
+/* Get a value with a potential suffix (k/m/g/t/p/e) */
+static unsigned long long suffix_number(const char *str)
+{
+  char *ep;
+  unsigned long long v;
+  int shift;
+
+  v = strtoull(str, &ep, 0);
+  switch (*ep|0x20) {
+  case 'k':
+    shift = 10;
+    break;
+  case 'm':
+    shift = 20;
+    break;
+  case 'g':
+    shift = 30;
+    break;
+  case 't':
+    shift = 40;
+    break;
+  case 'p':
+    shift = 50;
+    break;
+  case 'e':
+    shift = 60;
+    break;
+  default:
+    shift = 0;
+    break;
+  }
+  v <<= shift;
+
+  return v;
+}
+
+/* Stitch together the command line from a set of argv's */
+static char *make_cmdline(char **argv)
+{
+  char **arg;
+  size_t bytes;
+  char *cmdline, *p;
+
+  bytes = 1;                   /* Just in case we have a zero-entry cmdline */
+  for (arg = argv; *arg; arg++) {
+    bytes += strlen(*arg)+1;
+  }
+
+  p = cmdline = malloc(bytes);
+  if (!cmdline)
+    return NULL;
+
+  for (arg = argv; *arg; arg++) {
+    int len = strlen(*arg);
+    memcpy(p, *arg, len);
+    p[len] = ' ';
+    p += len+1;
+  }
+
+  if (p > cmdline)
+    p--;                       /* Remove the last space */
+  *p = '\0';
+
+  return cmdline;
+}
+
+int main(int argc, char *argv[])
+{
+  uint32_t mem_limit = 0;
+  uint16_t video_mode = 0;
+  const char *kernel_name;
+  struct initramfs *initramfs;
+  char *cmdline;
+  char *boot_image;
+  void *kernel_data;
+  size_t kernel_len;
+  int opt_dhcpinfo = 0;
+  void *dhcpdata;
+  size_t dhcplen;
+  char **argp, *arg, *p;
+
+  openconsole(&dev_null_r, &dev_stdcon_w);
+
+  (void)argc;
+  argp = argv+1;
+
+  while ((arg = *argp) && arg[0] == '-') {
+    if (!strcmp("-dhcpinfo", arg)) {
+      opt_dhcpinfo = 1;
+    } else {
+      fprintf(stderr, "%s: unknown option: %s\n", progname, arg);
+      return 1;
+    }
+  }
+
+  if (!arg) {
+    fprintf(stderr, "%s: missing kernel name\n", progname);
+    return 1;
+  }
+
+  kernel_name = arg;
+
+  printf("Loading %s... ", kernel_name);
+  if (loadfile(kernel_name, &kernel_data, &kernel_len)) {
+    printf("failed!\n");
+    goto bail;
+  }
+  printf("ok\n");
+
+  boot_image = malloc(strlen(kernel_name)+12);
+  if (!boot_image)
+    goto bail;
+
+  strcpy(boot_image, "BOOT_IMAGE=");
+  strcpy(boot_image+11, kernel_name);
+
+  /* argp now points to the kernel name, and the command line follows.
+     Overwrite the kernel name with the BOOT_IMAGE= argument, and thus
+     we have the final argument. */
+  *argp = boot_image;
+
+  cmdline = make_cmdline(argp);
+  if (!cmdline)
+    goto bail;
+
+  /* Initialize the initramfs chain */
+  initramfs = initramfs_init();
+  if (!initramfs)
+    goto bail;
+
+  /* Look for specific command-line arguments we care about */
+  if ((arg = find_argument(argp, "mem=")))
+    mem_limit = suffix_number(arg);
+
+  if ((arg = find_argument(argp, "vga=")))
+    video_mode = strtoul(arg, NULL, 0);
+
+  if ((arg = find_argument(argp, "initrd="))) {
+    do {
+      p = strchr(arg, ',');
+      if (p)
+       *p = '\0';
+
+      printf("Loading %s... ", arg);
+      if (initramfs_load_archive(initramfs, arg)) {
+       printf("failed!\n");
+       goto bail;
+      }
+      printf("ok\n");
+
+      if (p)
+       *p++ = ',';
+    } while ((arg = p));
+  }
+
+  /* Append the DHCP info */
+  if (opt_dhcpinfo &&
+      !pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) {
+    if (initramfs_add_file(initramfs, dhcpdata, dhcplen, dhcplen,
+                          "/dhcpinfo.dat", 0, 0755))
+      goto bail;
+  }
+
+  /* This should not return... */
+  syslinux_boot_linux(kernel_data, kernel_len, initramfs, cmdline,
+                     video_mode, mem_limit);
+
+ bail:
+  fprintf(stderr, "Kernel load failure (insufficient memory?)\n");
+  return 1;
+}