ifcpu64.c32: simple module to choose a 32, 32pae, or 64-bit kernel
[people/sha0/syslinux.git] / com32 / modules / ifcpu64.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2008 H. Peter Anvin - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12
13 /*
14  * ifcpu64.c
15  *
16  * Run one command if the CPU has 64-bit support, and another if it doesn't.
17  * Eventually this and other features should get folded into some kind
18  * of scripting engine.
19  *
20  * Usage:
21  *
22  *    label boot_kernel
23  *        kernel ifcpu64.c
24  *        append boot_kernel_64 [-- boot_kernel_32pae] -- boot_kernel_32
25  *    label boot_kernel_32
26  *        kernel vmlinuz_32
27  *        append ...
28  *    label boot_kernel_64
29  *        kernel vmlinuz_64
30  *        append ...
31  */
32
33 #include <alloca.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <cpuid.h>
37 #include <syslinux/boot.h>
38
39 static bool cpu_has_cpuid(void)
40 {
41   return cpu_has_eflag(X86_EFLAGS_ID);
42 }
43
44 static bool cpu_has_level(uint32_t level)
45 {
46   uint32_t group = level & 0xffff0000;
47   uint32_t limit = cpuid_eax(group);
48   if ((limit & 0xffff0000) != group)
49     return false;
50   if (level > limit)
51     return false;
52
53   return true;
54 }
55
56 static bool cpu_has_pae(void)
57 {
58   if (!cpu_has_cpuid())
59     return false;
60
61   if (!cpu_has_level(0x00000001))
62     return false;
63
64   return !!(cpuid_edx(0x00000001) & (X86_FEATURE_PAE & 31));
65 }
66
67 static bool cpu_has_lm(void)
68 {
69   if (!cpu_has_cpuid())
70     return false;
71
72   if (!cpu_has_level(0x80000001))
73     return false;
74
75   return !!(cpuid_edx(0x80000001) & (X86_FEATURE_LM & 31));
76 }
77
78 /* XXX: this really should be librarized */
79 static void boot_args(char **args)
80 {
81   int len = 0;
82   char **pp;
83   const char *p;
84   char c, *q, *str;
85
86   for (pp = args; *pp; pp++)
87     len += strlen(*pp);
88
89   q = str = alloca(len+1);
90   for (pp = args; *pp; pp++) {
91     p = *pp;
92     while ((c = *p++))
93       *q++ = c;
94   }
95   *q = '\0';
96
97   if (!str[0])
98     syslinux_run_default();
99   else
100     syslinux_run_command(str);
101 }
102
103 int main(int argc, char *argv[])
104 {
105   char **args[3];
106   int i;
107   int n;
108
109   for (i = 0; i < 3; i++)
110     args[i] = &argv[1];
111
112   n = 1;
113   for (i = 1; i < argc; i++) {
114     if (!strcmp(argv[i], "--")) {
115       argv[i] = NULL;
116       args[n++] = &argv[i+1];
117     }
118     if (n >= 3)
119       break;
120   }
121
122   boot_args(cpu_has_lm()  ? args[0] :
123             cpu_has_pae() ? args[1] :
124             args[2]);
125   return -1;
126 }