Handle mem= and vga= in the Linux kernel command line.
authorH. Peter Anvin <hpa@zytor.com>
Sat, 5 Jan 2008 00:24:47 +0000 (16:24 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Sat, 5 Jan 2008 00:24:47 +0000 (16:24 -0800)
linux.c
setup.h

diff --git a/linux.c b/linux.c
index 93fd62e..97f0b6d 100644 (file)
--- a/linux.c
+++ b/linux.c
@@ -16,6 +16,7 @@
  * Actually wrap the kernel image...
  */
 
+#include <ctype.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <getopt.h>
 extern char reloc[];
 extern uint32_t reloc_size;
 
+/* Find the last instance of a particular command line argument
+   (which should include the final =; do not use for boolean arguments) */
+static const char *find_argument(const char *cmdline, const char *argument)
+{
+  int la = strlen(argument);
+  const char *p = cmdline;
+  int wasspace = 1;
+  
+  while (*p) {
+    if (wasspace && !memcmp(p, argument, la))
+      return p+la;
+
+    wasspace = isspace(*p++);
+  }
+
+  return NULL;
+}
+
+/* Truncate to 32 bits, with saturate */
+static inline uint32_t saturate32(unsigned long long v)
+{
+  return (v > 0xffffffff) ? 0xffffffff : (uint32_t)v;
+}
+
+/* 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;
+}
+
 int wrap_kernel(const char *kernel_file, const char *cmdline,
                const struct string_list *initrd_list, FILE *out)
 {
@@ -59,6 +120,8 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
   const struct string_list *ip;
   int setup_ver;               /* Setup protocol version */
   int setup_space;             /* How much space for the setup */
+  const char *cmd;
+  uint32_t initrd_max;
 
   /* Process the kernel file */
 
@@ -88,7 +151,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
   if (setup_len == 512)
     setup_len += 2048;         /* Really old kernel */
 
-  if (rdle32(&hdr->header) != 0x53726448)
+  if (rdle32(&hdr->header) != LINUX_MAGIC)
     setup_ver = 0;             /* Ancient kernel */
   else
     setup_ver = rdle16(&hdr->version);
@@ -106,6 +169,37 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
   if (setup_ver <= 0x200)
     initrd_list = NULL;                /* No initrd for ancient kernel */
 
+  if (setup_ver >= 0x203)
+    initrd_max = rdle32(&hdr->initrd_addr_max);
+  else
+    initrd_max = 0x37ffffff;
+
+  if ((cmd = find_argument(cmdline, "mem="))) {
+    uint32_t mem = saturate32(suffix_number(cmd));
+    if (initrd_max >= mem)
+      initrd_max = mem-1;
+  }
+
+  if ((cmd = find_argument(cmdline, "vga="))) {
+    uint16_t vga;
+
+    switch (cmd[0] | 0x20) {
+    case 'a':                  /* "ask" */
+      vga = 0xfffd;
+      break;
+    case 'e':                  /* "ext" */
+      vga = 0xfffe;
+      break;
+    case 'n':                  /* "normal" */
+      vga = 0xffff;
+      break;
+    default:
+      vga = strtoul(cmd, NULL, 0);
+      break;
+    }
+    wrle16(vga, &hdr->vid_mode);
+  }
+
   /* Process the initrd file(s) */
   
   ninitrd = 0;
@@ -182,7 +276,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
     wrle32(scmd.address, &hdr->cmd_line_ptr);
   } else {
     /* Old-style command line protocol */
-    wrle16(0xA33F, (uint16_t *)(kernel+0x20));
+    wrle16(OLD_CMDLINE_MAGIC, (uint16_t *)(kernel+0x20));
     wrle16(scmd.address - ssup.address, (uint16_t *)(kernel+0x22));
   }
   if (setup_ver >= 0x201) {
@@ -222,10 +316,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
   /* Set up the startup info */
   wrle32(ninitrd ? ird[0].seg.address : 0, &info->rd_addr);
   wrle32(initrd_len, &info->rd_len);
-  if (setup_ver >= 0x203)
-    wrle32(rdle32(&hdr->initrd_addr_max), &info->rd_maxaddr);
-  else
-    wrle32(0x37ffffff, &info->rd_maxaddr);
+  wrle32(initrd_max, &info->rd_maxaddr);
   wrle32(ssup.address, &info->setup_addr);
   wrle32(scmd.address, &info->cmdline_addr);
 
diff --git a/setup.h b/setup.h
index baf008d..a28a94c 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -12,6 +12,9 @@ struct startup_info {
        uint32_t reloc_size;
 };
 
+#define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24))
+#define OLD_CMDLINE_MAGIC 0xA33F
+
 struct setup_header {
        uint8_t  setup_sects;
        uint16_t root_flags;