ifcpu64.c32: simple module to choose a 32, 32pae, or 64-bit kernel
authorH. Peter Anvin <hpa@zytor.com>
Wed, 16 Jul 2008 22:21:21 +0000 (15:21 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Wed, 16 Jul 2008 22:24:57 +0000 (15:24 -0700)
A very simple module to choose between a 32-bit, 32-bit PAE, or a
64-bit kernel depending on the capabilities of the CPU.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
NEWS
com32/modules/Makefile
com32/modules/ifcpu64.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index b8f4453..3694ae7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,10 @@ Changes in 3.71:
          partition types (01, 04, 06, 07, 0b, 0c, 0e).
        * Unbreak the KBDMAP command (broken in 3.70).
        * EXTLINUX: fix the handling of the ADV when using CBIOS.
+       * ifcpu64.c32: simple COM32 module to select a 32- or 64-bit
+         kernel (and optionally 32-bit kernels with or without PAE.)
+         Eventually we want a scripting language for this
+         kind of stuff...
 
 Changes in 3.70:
        * PXELINUX: Support enhanced capabilities when running on top
index dc114f5..6624a94 100644 (file)
@@ -54,7 +54,7 @@ COM32DIR = $(AUXDIR)/com32
 
 MODULES          = chain.c32 ethersel.c32 mboot.c32 dmitest.c32 cpuidtest.c32 \
            pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 meminfo.c32 \
-           sdi.c32 sanboot.c32
+           sdi.c32 sanboot.c32 ifcpu64.c32
 
 TESTFILES =
 
diff --git a/com32/modules/ifcpu64.c b/com32/modules/ifcpu64.c
new file mode 100644 (file)
index 0000000..def43b3
--- /dev/null
@@ -0,0 +1,126 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ifcpu64.c
+ *
+ * Run one command if the CPU has 64-bit support, and another if it doesn't.
+ * Eventually this and other features should get folded into some kind
+ * of scripting engine.
+ *
+ * Usage:
+ *
+ *    label boot_kernel
+ *        kernel ifcpu64.c
+ *        append boot_kernel_64 [-- boot_kernel_32pae] -- boot_kernel_32
+ *    label boot_kernel_32
+ *        kernel vmlinuz_32
+ *        append ...
+ *    label boot_kernel_64
+ *        kernel vmlinuz_64
+ *        append ...
+ */
+
+#include <alloca.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cpuid.h>
+#include <syslinux/boot.h>
+
+static bool cpu_has_cpuid(void)
+{
+  return cpu_has_eflag(X86_EFLAGS_ID);
+}
+
+static bool cpu_has_level(uint32_t level)
+{
+  uint32_t group = level & 0xffff0000;
+  uint32_t limit = cpuid_eax(group);
+  if ((limit & 0xffff0000) != group)
+    return false;
+  if (level > limit)
+    return false;
+
+  return true;
+}
+
+static bool cpu_has_pae(void)
+{
+  if (!cpu_has_cpuid())
+    return false;
+
+  if (!cpu_has_level(0x00000001))
+    return false;
+
+  return !!(cpuid_edx(0x00000001) & (X86_FEATURE_PAE & 31));
+}
+
+static bool cpu_has_lm(void)
+{
+  if (!cpu_has_cpuid())
+    return false;
+
+  if (!cpu_has_level(0x80000001))
+    return false;
+
+  return !!(cpuid_edx(0x80000001) & (X86_FEATURE_LM & 31));
+}
+
+/* XXX: this really should be librarized */
+static void boot_args(char **args)
+{
+  int len = 0;
+  char **pp;
+  const char *p;
+  char c, *q, *str;
+
+  for (pp = args; *pp; pp++)
+    len += strlen(*pp);
+
+  q = str = alloca(len+1);
+  for (pp = args; *pp; pp++) {
+    p = *pp;
+    while ((c = *p++))
+      *q++ = c;
+  }
+  *q = '\0';
+
+  if (!str[0])
+    syslinux_run_default();
+  else
+    syslinux_run_command(str);
+}
+
+int main(int argc, char *argv[])
+{
+  char **args[3];
+  int i;
+  int n;
+
+  for (i = 0; i < 3; i++)
+    args[i] = &argv[1];
+
+  n = 1;
+  for (i = 1; i < argc; i++) {
+    if (!strcmp(argv[i], "--")) {
+      argv[i] = NULL;
+      args[n++] = &argv[i+1];
+    }
+    if (n >= 3)
+      break;
+  }
+
+  boot_args(cpu_has_lm()  ? args[0] :
+           cpu_has_pae() ? args[1] :
+           args[2]);
+  return -1;
+}