b902ebc5e4e42c1a2e666587ef07ae120f1d8a12
[profile/ivi/syslinux.git] / com32 / modules / linux.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
5  *
6  *   Permission is hereby granted, free of charge, to any person
7  *   obtaining a copy of this software and associated documentation
8  *   files (the "Software"), to deal in the Software without
9  *   restriction, including without limitation the rights to use,
10  *   copy, modify, merge, publish, distribute, sublicense, and/or
11  *   sell copies of the Software, and to permit persons to whom
12  *   the Software is furnished to do so, subject to the following
13  *   conditions:
14  *
15  *   The above copyright notice and this permission notice shall
16  *   be included in all copies or substantial portions of the Software.
17  *
18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * ----------------------------------------------------------------------- */
28
29 /*
30  * linux.c
31  *
32  * Sample module to load Linux kernels.  This module can also create
33  * a file out of the DHCP return data if running under PXELINUX.
34  *
35  * If -dhcpinfo is specified, the DHCP info is written into the file
36  * /dhcpinfo.dat in the initramfs.
37  *
38  * Usage: linux.c32 [-dhcpinfo] kernel arguments...
39  */
40
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <console.h>
46 #include <syslinux/loadfile.h>
47 #include <syslinux/linux.h>
48 #include <syslinux/pxe.h>
49
50 const char *progname = "linux.c32";
51
52 /* Find the last instance of a particular command line argument
53    (which should include the final =; do not use for boolean arguments) */
54 static char *find_argument(char **argv, const char *argument)
55 {
56     int la = strlen(argument);
57     char **arg;
58     char *ptr = NULL;
59
60     for (arg = argv; *arg; arg++) {
61         if (!memcmp(*arg, argument, la))
62             ptr = *arg + la;
63     }
64
65     return ptr;
66 }
67
68 /* Search for a boolean argument; return its position, or 0 if not present */
69 static int find_boolean(char **argv, const char *argument)
70 {
71     char **arg;
72
73     for (arg = argv; *arg; arg++) {
74         if (!strcmp(*arg, argument))
75             return (arg - argv) + 1;
76     }
77
78     return 0;
79 }
80
81 /* Stitch together the command line from a set of argv's */
82 static char *make_cmdline(char **argv)
83 {
84     char **arg;
85     size_t bytes;
86     char *cmdline, *p;
87
88     bytes = 1;                  /* Just in case we have a zero-entry cmdline */
89     for (arg = argv; *arg; arg++) {
90         bytes += strlen(*arg) + 1;
91     }
92
93     p = cmdline = malloc(bytes);
94     if (!cmdline)
95         return NULL;
96
97     for (arg = argv; *arg; arg++) {
98         int len = strlen(*arg);
99         memcpy(p, *arg, len);
100         p[len] = ' ';
101         p += len + 1;
102     }
103
104     if (p > cmdline)
105         p--;                    /* Remove the last space */
106     *p = '\0';
107
108     return cmdline;
109 }
110
111 int main(int argc, char *argv[])
112 {
113     const char *kernel_name;
114     struct initramfs *initramfs;
115     char *cmdline;
116     char *boot_image;
117     void *kernel_data;
118     size_t kernel_len;
119     bool opt_dhcpinfo = false;
120     bool opt_quiet = false;
121     void *dhcpdata;
122     size_t dhcplen;
123     char **argp, *arg, *p;
124
125     openconsole(&dev_null_r, &dev_stdcon_w);
126
127     (void)argc;
128     argp = argv + 1;
129
130     while ((arg = *argp) && arg[0] == '-') {
131         if (!strcmp("-dhcpinfo", arg)) {
132             opt_dhcpinfo = true;
133         } else {
134             fprintf(stderr, "%s: unknown option: %s\n", progname, arg);
135             return 1;
136         }
137         argp++;
138     }
139
140     if (!arg) {
141         fprintf(stderr, "%s: missing kernel name\n", progname);
142         return 1;
143     }
144
145     kernel_name = arg;
146
147     boot_image = malloc(strlen(kernel_name) + 12);
148     if (!boot_image)
149         goto bail;
150     strcpy(boot_image, "BOOT_IMAGE=");
151     strcpy(boot_image + 11, kernel_name);
152     /* argp now points to the kernel name, and the command line follows.
153        Overwrite the kernel name with the BOOT_IMAGE= argument, and thus
154        we have the final argument. */
155     *argp = boot_image;
156
157     if (find_boolean(argp, "quiet"))
158         opt_quiet = true;
159
160     if (!opt_quiet)
161         printf("Loading %s... ", kernel_name);
162     if (loadfile(kernel_name, &kernel_data, &kernel_len)) {
163         if (opt_quiet)
164             printf("Loading %s ", kernel_name);
165         printf("failed!\n");
166         goto bail;
167     }
168     if (!opt_quiet)
169         printf("ok\n");
170
171     cmdline = make_cmdline(argp);
172     if (!cmdline)
173         goto bail;
174
175     /* Initialize the initramfs chain */
176     initramfs = initramfs_init();
177     if (!initramfs)
178         goto bail;
179
180     if ((arg = find_argument(argp, "initrd="))) {
181         do {
182             p = strchr(arg, ',');
183             if (p)
184                 *p = '\0';
185
186             if (!opt_quiet)
187                 printf("Loading %s... ", arg);
188             if (initramfs_load_archive(initramfs, arg)) {
189                 if (opt_quiet)
190                     printf("Loading %s ", kernel_name);
191                 printf("failed!\n");
192                 goto bail;
193             }
194             if (!opt_quiet)
195                 printf("ok\n");
196
197             if (p)
198                 *p++ = ',';
199         } while ((arg = p));
200     }
201
202     /* Append the DHCP info */
203     if (opt_dhcpinfo &&
204         !pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) {
205         if (initramfs_add_file(initramfs, dhcpdata, dhcplen, dhcplen,
206                                "/dhcpinfo.dat", 0, 0755))
207             goto bail;
208     }
209
210     /* This should not return... */
211     syslinux_boot_linux(kernel_data, kernel_len, initramfs, cmdline);
212
213 bail:
214     fprintf(stderr, "Kernel load failure (insufficient memory?)\n");
215     return 1;
216 }