aboutsummaryrefslogtreecommitdiffstats
path: root/com32/elflink/ldlinux/ldlinux.c
diff options
context:
space:
mode:
Diffstat (limited to 'com32/elflink/ldlinux/ldlinux.c')
-rw-r--r--com32/elflink/ldlinux/ldlinux.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/com32/elflink/ldlinux/ldlinux.c b/com32/elflink/ldlinux/ldlinux.c
new file mode 100644
index 00000000..76d117c7
--- /dev/null
+++ b/com32/elflink/ldlinux/ldlinux.c
@@ -0,0 +1,349 @@
+#include <linux/list.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include "cli.h"
+#include "console.h"
+#include "com32.h"
+#include "menu.h"
+#include "config.h"
+#include "syslinux/adv.h"
+#include "syslinux/boot.h"
+#include "syslinux/config.h"
+
+#include <sys/module.h>
+
+struct file_ext {
+ const char *name;
+ enum kernel_type type;
+};
+
+static const struct file_ext file_extensions[] = {
+ { ".c32", IMAGE_TYPE_COM32 },
+ { ".img", IMAGE_TYPE_FDIMAGE },
+ { ".bss", IMAGE_TYPE_BSS },
+ { ".bin", IMAGE_TYPE_BOOT },
+ { ".bs", IMAGE_TYPE_BOOT },
+ { ".0", IMAGE_TYPE_PXE },
+ { NULL, 0 },
+};
+
+/*
+ * Return a pointer to one byte after the last character of the
+ * command.
+ */
+static inline const char *find_command(const char *str)
+{
+ const char *p;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+ return p;
+}
+
+__export uint32_t parse_image_type(const char *kernel)
+{
+ const struct file_ext *ext;
+ const char *p;
+ int len;
+
+ /* Find the end of the command */
+ p = find_command(kernel);
+ len = p - kernel;
+
+ for (ext = file_extensions; ext->name; ext++) {
+ int elen = strlen(ext->name);
+
+ if (!strncmp(kernel + len - elen, ext->name, elen))
+ return ext->type;
+ }
+
+ /* use IMAGE_TYPE_KERNEL as default */
+ return IMAGE_TYPE_KERNEL;
+}
+
+/*
+ * Returns the kernel name with file extension if one wasn't present.
+ */
+static const char *get_extension(const char *kernel)
+{
+ const struct file_ext *ext;
+ const char *p;
+ int len;
+
+ /* Find the end of the command */
+ p = find_command(kernel);
+ len = p - kernel;
+
+ for (ext = file_extensions; ext->name; ext++) {
+ char *str;
+ int elen = strlen(ext->name);
+ FILE *f;
+
+ str = malloc(len + elen + 1);
+
+ strncpy(str, kernel, len);
+ strncpy(str + len, ext->name, elen);
+ str[len + elen] = '\0';
+ f = findpath(str);
+ free(str);
+
+ if (f) {
+ fclose(f);
+ return ext->name;
+ }
+ }
+
+ return NULL;
+}
+
+const char *apply_extension(const char *kernel, const char *ext)
+{
+ const char *p;
+ char *k;
+ int len = strlen(kernel);
+ int elen = strlen(ext);
+
+ k = malloc(len + elen + 1);
+ if (!k)
+ return NULL;
+
+ p = find_command(kernel);
+
+ len = p - kernel;
+
+ /* Copy just the kernel name */
+ memcpy(k, kernel, len);
+
+ /* Append the extension */
+ if (strncmp(p - elen, ext, elen)) {
+ memcpy(k + len, ext, elen);
+ len += elen;
+ }
+
+ /* Copy the rest of the command line */
+ strcpy(k + len, p);
+
+ k[len + strlen(p)] = '\0';
+
+ return k;
+}
+
+/*
+ * Attempt to load a kernel after deciding what type of image it is.
+ *
+ * We only return from this function if something went wrong loading
+ * the the kernel. If we return the caller should call enter_cmdline()
+ * so that the user can help us out.
+ */
+__export void load_kernel(const char *command_line)
+{
+ struct menu_entry *me;
+ const char *cmdline;
+ const char *kernel;
+ uint32_t type;
+
+ kernel = strdup(command_line);
+ if (!kernel)
+ goto bad_kernel;
+
+ /* Virtual kernel? */
+ me = find_label(kernel);
+ if (me) {
+ const char *args;
+ char *cmd;
+ size_t len = strlen(me->cmdline) + 1;
+
+ /* Find the end of the command */
+ args = find_command(kernel);
+ while(*args && my_isspace(*args))
+ args++;
+
+ if (strlen(args))
+ len += strlen(args) + 1; /* +1 for space (' ') */
+
+ cmd = malloc(len);
+ if (!cmd)
+ goto bad_kernel;
+
+ if (strlen(args))
+ snprintf(cmd, len, "%s %s", me->cmdline, args);
+ else
+ strncpy(cmd, me->cmdline, len);
+
+ type = parse_image_type(cmd);
+ execute(cmd, type, false);
+ /* We shouldn't return */
+ goto bad_kernel;
+ }
+
+ if (!allowimplicit)
+ goto bad_implicit;
+
+ /* Insert a null character to ignore any user-specified options */
+ if (!allowoptions) {
+ char *p = (char *)find_command(kernel);
+ *p = '\0';
+ }
+
+ type = parse_image_type(kernel);
+ if (type == IMAGE_TYPE_KERNEL) {
+ const char *ext;
+
+ /*
+ * Automatically lookup the extension if one wasn't
+ * supplied by the user.
+ */
+ ext = get_extension(kernel);
+ if (ext) {
+ const char *k;
+
+ k = apply_extension(kernel, ext);
+ if (!k)
+ goto bad_kernel;
+
+ free((void *)kernel);
+ kernel = k;
+
+ type = parse_image_type(kernel);
+ }
+ }
+
+ execute(kernel, type, true);
+ free((void *)kernel);
+
+bad_implicit:
+bad_kernel:
+ /*
+ * If we fail to boot the kernel execute the "onerror" command
+ * line.
+ */
+ if (onerrorlen) {
+ me = find_label(onerror);
+ if (me)
+ rsprintf(&cmdline, "%s %s", me->cmdline, default_cmd);
+ else
+ rsprintf(&cmdline, "%s %s", onerror, default_cmd);
+
+ type = parse_image_type(cmdline);
+ execute(cmdline, type, true);
+ }
+}
+
+/*
+ * If this function returns you must call ldinux_enter_command() to
+ * preserve the 4.0x behaviour.
+ */
+void ldlinux_auto_boot(void)
+{
+ if (!defaultlevel) {
+ if (strlen(ConfigName))
+ printf("No DEFAULT or UI configuration directive found!\n");
+ if (noescape)
+ kaboom();
+ } else
+ load_kernel(default_cmd);
+}
+
+static void enter_cmdline(void)
+{
+ const char *cmdline;
+
+ /* Enter endless command line prompt, should support "exit" */
+ while (1) {
+ bool to = false;
+
+ if (noescape) {
+ ldlinux_auto_boot();
+ continue;
+ }
+
+ cmdline = edit_cmdline("boot:", 1, NULL, cat_help_file, &to);
+ printf("\n");
+
+ /* return if user only press enter or we timed out */
+ if (!cmdline || cmdline[0] == '\0') {
+ if (to && ontimeoutlen)
+ load_kernel(ontimeout);
+ else
+ ldlinux_auto_boot();
+ } else
+ load_kernel(cmdline);
+ }
+}
+
+void ldlinux_enter_command(void)
+{
+ enter_cmdline();
+}
+
+/*
+ * Undo the work we did in openconsole().
+ */
+static void __destructor close_console(void)
+{
+ int i;
+
+ for (i = 0; i <= 2; i++)
+ close(i);
+}
+
+void ldlinux_console_init(void)
+{
+ openconsole(&dev_stdcon_r, &dev_ansiserial_w);
+}
+
+__export int main(int argc __unused, char **argv)
+{
+ const void *adv;
+ const char *cmdline;
+ size_t count = 0;
+
+ ldlinux_console_init();
+
+ parse_configs(&argv[1]);
+
+ __syslinux_set_serial_console_info();
+
+ adv = syslinux_getadv(ADV_BOOTONCE, &count);
+ if (adv && count) {
+ /*
+ * We apparently have a boot-once set; clear it and
+ * then execute the boot-once.
+ */
+ char *src, *dst;
+ size_t i;
+
+ src = (char *)adv;
+ cmdline = dst = malloc(count + 1);
+ if (!dst) {
+ printf("Failed to allocate memory for ADV\n");
+ ldlinux_enter_command();
+ }
+
+ for (i = 0; i < count; i++)
+ *dst++ = *src++;
+ *dst = '\0'; /* Null-terminate */
+
+ /* Clear the boot-once data from the ADV */
+ if (!syslinux_setadv(ADV_BOOTONCE, 0, NULL))
+ syslinux_adv_write();
+
+ load_kernel(cmdline); /* Shouldn't return */
+ ldlinux_enter_command();
+ }
+
+ /* TODO: Check KbdFlags? */
+ if (!forceprompt)
+ ldlinux_auto_boot();
+
+ if (defaultlevel > 1)
+ ldlinux_auto_boot();
+
+ ldlinux_enter_command();
+ return 0;
+}