6 #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
9 #include <sys/utsname.h>
12 #include "qi-bootmenu.h"
14 #include "fstype/fstype.h"
16 #ifndef COMMAND_LINE_SIZE
17 # define COMMAND_LINE_SIZE 256
20 #define MOUNTPOINT "/mnt"
21 #define MODPROBE "/sbin/modprobe"
22 #define KEXEC "/sbin/kexec"
23 /* XXX: use --command-line= instead? */
24 #define KEXEC_CMDLINE "--append="
26 /* filesystems built into the kernel */
27 static Eina_List
*fs_core
;
28 /* filesystems supported by kernel modules */
29 static Eina_List
*fs_mods
;
30 /* partitions read from /proc/partitions */
31 static Eina_List
*partitions
;
32 /* systems which were found to boot */
33 static Eina_List
*systems
;
35 static Eina_List
* get_partitions() {
36 FILE *f
= fopen("/proc/partitions", "r");
41 Eina_List
*partitions
= NULL
;
46 fgets(line
, sizeof(line
), f
);
47 fgets(line
, sizeof(line
), f
);
49 while (fgets(line
, sizeof(line
), f
)) {
50 char *dev
, *q
, *p
= line
;
53 /* skip first two columns */
58 /* read number of blocks */
59 blocks
= strtol(p
, &q
, 10);
60 if (!blocks
|| p
== q
)
62 /* ignore small partitions < 10MB */
63 if (blocks
< 10 * 1024)
65 /* read partition name and remove trailing \n */
72 dev
= malloc(sstrlen("/dev/") + len
+ 1);
75 partitions
= eina_list_append(partitions
, dev
);
82 static Eina_List
* get_kernel_filesystems_builtin() {
83 FILE *f
= fopen("/proc/filesystems", "r");
92 while (fgets(line
, sizeof(line
), f
)) {
93 /* ignore lines starting with nodev */
94 if (!strncmp(line
, "nodev", sstrlen("nodev")))
96 line
[strlen(line
) - 1] = '\0';
97 /* skip tab with line + 1*/
98 fs
= eina_list_append(fs
, strdup(line
+ 1));
105 static Eina_List
* get_kernel_filesystems_modules() {
107 #ifdef CONFIG_SUPPORT_KERNEL_FS_MODULES
109 Eina_List
*fs
= NULL
;
112 char buf
[1024], *p
, *q
;
118 len
= snprintf(buf
, sizeof(buf
), "/lib/modules/%s/modules.dep", uts
.release
);
127 /* calculate the length of the prefix for the modules, this assumes that the
128 * modules are installed under the directroy where we found the modules.dep file.
131 len
-= sstrlen("modules.dep");
133 /* Maybe we should readdir("/lib/modules/`uname -r`/kernel/fs") instead? */
135 while (fgets(buf
, sizeof(buf
), f
)) {
136 /* skip lines which don't contain a colon */
137 if (!(p
= strchr(buf
, ':')))
139 /* we are only interested in the part before '.ko:' */
140 *(p
- sstrlen(".ko")) = '\0';
143 /* skip the prefix '/lib/modules/`uname -r`/' */
146 if (strncmp(p
, "kernel/fs/", sstrlen("kernel/fs/")))
149 q
= p
+= sstrlen("kernel/fs/");
154 /* XXX: check for duplicates? */
155 fs
= eina_list_append(fs
, strdup(p
));
162 #endif /* CONFIG_SUPPORT_KERNEL_FS_MODULES */
165 static bool is_supported_filesystem(const char *fs
) {
167 if (eina_list_search_unsorted(fs_core
, EINA_COMPARE_CB(strcmp
), fs
))
170 #ifdef CONFIG_SUPPORT_KERNEL_FS_MODULES
171 const char *modprobe
[] = { MODPROBE
, fs
};
172 if (eina_list_search_unsorted(fs_mods
, EINA_COMPARE_CB(strcmp
), fs
) &&
173 fexecw(modprobe
[0], (char *const *)modprobe
, NULL
) == 0) {
174 debug("Successfully loaded kernel filesystem module: '%s'\n", fs
);
175 fs_mods
= eina_list_remove(fs_mods
, fs
);
176 fs_core
= eina_list_append(fs_core
, fs
);
184 Eina_List
* scan_system() {
187 const char *dev
, *mnt
, *fs
;
188 char buf
[COMMAND_LINE_SIZE
];
191 /* check if we need to read data from /proc first */
193 partitions
= get_partitions();
195 fs_core
= get_kernel_filesystems_builtin();
197 fs_mods
= get_kernel_filesystems_modules();
199 /* XXX: doesn't handle sub directories assumes /mnt */
200 mkdir(MOUNTPOINT
, 0755);
201 /* chdir so we can use relative paths for mount(2) */
202 if (chdir(MOUNTPOINT
)) {
207 EINA_LIST_FOREACH(partitions
, l
, dev
) {
209 int fd
= open(dev
, O_RDONLY
);
215 if (identify_fs(fd
, &fs
, NULL
, 0)) {
216 eprint("Couldn't identify filesystem on '%s'\n", dev
);
220 if (!is_supported_filesystem(fs
)) {
221 eprint("Unsupported filesystem '%s' on '%s'\n", fs
, dev
);
225 /* we chdir()-ed and now use relative paths */
226 mnt
= dev
+ sstrlen("/dev/");
227 if (mkdir(mnt
, 0755) && errno
!= EEXIST
) {
232 if (mount(dev
, mnt
, fs
, MS_RDONLY
, NULL
) && errno
!= EBUSY
) {
237 /* XXX: replace fixed GTA02 with machine type */
238 snprintf(buf
, sizeof buf
, "%s/%s/boot/uImage-GTA02.bin", MOUNTPOINT
, mnt
);
239 if (stat(buf
, &st
)) {
240 /* no uImage present now check for zImage */
241 buf
[sstrlen(MOUNTPOINT
) + sstrlen("/") + strlen(mnt
) + sstrlen("/boot/")] = 'z';
243 if (stat(buf
, &st
)) {
244 eprint("No kernel found at '%s'\n", buf
);
250 BootItem
*sys
= calloc(sizeof(BootItem
), 1);
251 sys
->kernel
= strdup(buf
);
254 /* XXX: replace fixed GTA02 with machine type */
255 snprintf(buf
, sizeof buf
, "%s/%s/boot/append-GTA02", MOUNTPOINT
, mnt
);
256 FILE *f
= fopen(buf
, "r");
258 fgets(buf
, sizeof buf
, f
);
259 sys
->cmdline
= strdup(buf
);
263 snprintf(buf
, sizeof buf
, "%s/%s/boot/bootlogo.png", MOUNTPOINT
, mnt
);
265 sys
->logo
= strdup(buf
);
267 systems
= eina_list_append(systems
, sys
);
276 /* Genereate kernel command line for use with kexec. It consist of the following:
278 * - /proc/cmdline of the running system
280 * - append-$MACHINE in the same directory as the kernel
283 char* get_kernel_cmdline(BootItem
*i
) {
284 static char cmdline
[2 * COMMAND_LINE_SIZE
] = KEXEC_CMDLINE
, *s
= cmdline
+ sstrlen(KEXEC_CMDLINE
);
286 /* read the cmdline of the currently running system only once */
287 if (!cmdline
[sstrlen(KEXEC_CMDLINE
) + 1]) {
288 FILE *f
= fopen("/proc/cmdline","r");
289 if (f
&& fgets(s
, sizeof(cmdline
) - sstrlen(KEXEC_CMDLINE
), f
)) {
290 /* /proc/cmdline ends with a new line which we want to overwrite thus -1 */
296 /* append root=/dev/... and the image specific command line */
297 int len
= snprintf(s
, sizeof(cmdline
) - (s
- cmdline
), "root=%s %s", i
->dev
, i
->cmdline
);
298 if (len
> 0 && s
[len
- 1] == '\n')
304 bool boot_kernel(BootItem
*i
) {
308 /* umount all filesystem we probed except the one where the kernel is located */
309 EINA_LIST_FOREACH(systems
, l
, s
) {
311 /* XXX: assumes we are still chdir()-ed */
312 umount(s
->dev
+ sstrlen("/dev/"));
316 const char *kexec_load
[] = { KEXEC
, get_kernel_cmdline(i
), "-l", i
->kernel
, NULL
};
317 if (fexecw(kexec_load
[0], (char *const *)kexec_load
, NULL
)) {
318 eprint("Couldn't load kernel from '%s'\n", i
->kernel
);
322 const char *kexec_exec
[] = { KEXEC
, "-e", NULL
};
324 /* XXX: assumes we are still chdir()-ed */
325 umount(i
->dev
+ sstrlen("/dev/"));
327 if (execve(kexec_exec
[0], (char *const *)kexec_exec
, NULL
)) {
328 eprint("Couldn't exec kernel '%s'\n", i
->kernel
);
332 /* can't be reached just here to silence compiler warning */
342 partitions
= get_partitions();
344 EINA_LIST_FOREACH(partitions
, l
, p
) {
348 puts("Built in filesystems:");
349 fs_core
= get_kernel_filesystems_builtin();
351 EINA_LIST_FOREACH(fs_core
, l
, p
) {
355 #ifdef CONFIG_SUPPORT_KERNEL_FS_MODULES
356 puts("Filesystem modules:");
357 fs_mods
= get_kernel_filesystems_modules();
359 EINA_LIST_FOREACH(fs_mods
, l
, p
) {
364 puts("Bootable images:");
365 systems
= scan_system();
367 EINA_LIST_FOREACH(systems
, l
, s
) {
368 printf("kexec %s'%s' -l %s\n", KEXEC_CMDLINE
,
369 get_kernel_cmdline(s
) + sstrlen(KEXEC_CMDLINE
), s
->kernel
);
370 printf("umount '%s'\n", s
->dev
);
371 printf("kexec -e\n\n");