summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in4
-rw-r--r--NEWS8
-rw-r--r--kern/boot_script.c788
-rw-r--r--kern/boot_script.h115
-rw-r--r--kern/bootstrap.c436
5 files changed, 1263 insertions, 88 deletions
diff --git a/Makefile.in b/Makefile.in
index 21abf57..04181e8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -139,7 +139,7 @@ kern-cfiles = act.c ast.c bootstrap.c counters.c debug.c eventcount.c \
machine.c pc_sample.c printf.c priority.c processor.c profile.c \
queue.c sched_prim.c startup.c strings.c syscall_emulation.c \
syscall_subr.c syscall_sw.c task.c thread.c thread_swap.c \
- time_stamp.c timer.c xpr.c zalloc.c elf-load.c
+ time_stamp.c timer.c xpr.c zalloc.c elf-load.c boot_script.c
kern-files = $(kern-cfiles) \
act.h assert.h ast.h compat_xxx_defs.h counters.h cpu_number.h \
debug.h eventcount.h host.h ipc_host.h ipc_kobject.h ipc_sched.h \
@@ -147,7 +147,7 @@ kern-files = $(kern-cfiles) \
pc_sample.h processor.h queue.h refcount.h sched.h sched_prim.h \
shuttle.h strings.h syscall_emulation.h syscall_subr.h syscall_sw.h \
task.h thread.h thread_swap.h time_out.h time_stamp.h timer.h xpr.h \
- zalloc.h \
+ zalloc.h boot_script.h \
mach.srv mach4.srv mach_debug.srv mach_host.srv
# Still more trivia
diff --git a/NEWS b/NEWS
index 695558f..b29e94a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,11 @@
-*- Text -*-
+
+The kernel now directly supports "boot scripts" in the form of multiboot
+module names with the same syntax as the Hurd's `serverboot' program.
+That is, instead of telling GRUB "module /boot/serverboot", you can give
+GRUB a series of command like "module /hurd/ext2fs ${...}" where the
+syntax after "module" is the same as in boot scripts for Hurd's `serverboot'.
+
The kernel message device `kmsg' is now enabled by default.
--disable-kmsg turns it off.
@@ -47,4 +54,3 @@ Bug in --enable-ncr53c7xx has been fixed.
Many thanks go to Marcus G. Daniels (marcus@cathcart.sysc.pdx.edu) for
his very helpful testing of the 1.0 release and for his many
improvements to the cross-compilation support.
-
diff --git a/kern/boot_script.c b/kern/boot_script.c
new file mode 100644
index 0000000..d031752
--- /dev/null
+++ b/kern/boot_script.c
@@ -0,0 +1,788 @@
+/* Boot script parser for Mach. */
+
+/* Written by Shantanu Goel (goel@cs.columbia.edu). */
+
+#include <mach/mach_types.h>
+#if !KERNEL || OSKIT_MACH
+#include <string.h>
+#endif
+#include "boot_script.h"
+
+
+/* This structure describes a symbol. */
+struct sym
+{
+ /* Symbol name. */
+ const char *name;
+
+ /* Type of value returned by function. */
+ int type;
+
+ /* Symbol value. */
+ int val;
+
+ /* For function symbols; type of value returned by function. */
+ int ret_type;
+
+ /* For function symbols; if set, execute function at the time
+ of command execution, not during parsing. A function with
+ this field set must also have `no_arg' set. Also, the function's
+ `val' argument will always be NULL. */
+ int run_on_exec;
+};
+
+/* Additional values symbols can take.
+ These are only used internally. */
+#define VAL_SYM 10 /* symbol table entry */
+#define VAL_FUNC 11 /* function pointer */
+
+/* This structure describes an argument. */
+struct arg
+{
+ /* Argument text copied verbatim. 0 if none. */
+ char *text;
+
+ /* Type of value assigned. 0 if none. */
+ int type;
+
+ /* Argument value. */
+ int val;
+};
+
+/* List of commands. */
+static struct cmd **cmds = 0;
+
+/* Amount allocated for `cmds'. */
+static int cmds_alloc = 0;
+
+/* Next available slot in `cmds'. */
+static int cmds_index = 0;
+
+/* Symbol table. */
+static struct sym **symtab = 0;
+
+/* Amount allocated for `symtab'. */
+static int symtab_alloc = 0;
+
+/* Next available slot in `symtab'. */
+static int symtab_index = 0;
+
+/* Create a task and suspend it. */
+static int
+create_task (struct cmd *cmd, int *val)
+{
+ int err = boot_script_task_create (cmd);
+ *val = (int) cmd->task;
+ return err;
+}
+
+/* Resume a task. */
+static int
+resume_task (struct cmd *cmd, int *val)
+{
+ return boot_script_task_resume (cmd);
+}
+
+/* Resume a task when the user hits return. */
+static int
+prompt_resume_task (struct cmd *cmd, int *val)
+{
+ return boot_script_prompt_task_resume (cmd);
+}
+
+/* List of builtin symbols. */
+static struct sym builtin_symbols[] =
+{
+ { "task-create", VAL_FUNC, (int) create_task, VAL_TASK, 0 },
+ { "task-resume", VAL_FUNC, (int) resume_task, VAL_NONE, 1 },
+ { "prompt-task-resume", VAL_FUNC, (int) prompt_resume_task, VAL_NONE, 1 },
+};
+#define NUM_BUILTIN (sizeof (builtin_symbols) / sizeof (builtin_symbols[0]))
+
+/* Free CMD and all storage associated with it.
+ If ABORTING is set, terminate the task associated with CMD,
+ otherwise just deallocate the send right. */
+static void
+free_cmd (struct cmd *cmd, int aborting)
+{
+ if (cmd->task)
+ boot_script_free_task (cmd->task, aborting);
+ if (cmd->args)
+ {
+ int i;
+ for (i = 0; i < cmd->args_index; i++)
+ boot_script_free (cmd->args[i], sizeof *cmd->args[i]);
+ boot_script_free (cmd->args, sizeof cmd->args[0] * cmd->args_alloc);
+ }
+ if (cmd->exec_funcs)
+ boot_script_free (cmd->exec_funcs,
+ sizeof cmd->exec_funcs[0] * cmd->exec_funcs_alloc);
+ boot_script_free (cmd, sizeof *cmd);
+}
+
+/* Free all storage allocated by the parser.
+ If ABORTING is set, terminate all tasks. */
+static void
+cleanup (int aborting)
+{
+ int i;
+
+ for (i = 0; i < cmds_index; i++)
+ free_cmd (cmds[i], aborting);
+ boot_script_free (cmds, sizeof cmds[0] * cmds_alloc);
+ cmds = 0;
+ cmds_index = cmds_alloc = 0;
+
+ for (i = 0; i < symtab_index; i++)
+ boot_script_free (symtab[i], sizeof *symtab[i]);
+ boot_script_free (symtab, sizeof symtab[0] * symtab_alloc);
+ symtab = 0;
+ symtab_index = symtab_alloc = 0;
+}
+
+/* Add PTR to the list of pointers PTR_LIST, which
+ currently has ALLOC amount of space allocated to it, and
+ whose next available slot is INDEX. If more space
+ needs to to allocated, INCR is the amount by which
+ to increase it. Return 0 on success, non-zero otherwise. */
+static int
+add_list (void *ptr, void ***ptr_list, int *alloc, int *index, int incr)
+{
+ if (*index == *alloc)
+ {
+ void **p;
+
+ *alloc += incr;
+ p = boot_script_malloc (*alloc * sizeof (void *));
+ if (! p)
+ {
+ *alloc -= incr;
+ return 1;
+ }
+ if (*ptr_list)
+ {
+ memcpy (p, *ptr_list, *index * sizeof (void *));
+ boot_script_free (*ptr_list, (*alloc - incr) * sizeof (void *));
+ }
+ *ptr_list = p;
+ }
+ *(*ptr_list + *index) = ptr;
+ *index += 1;
+ return 0;
+}
+
+/* Create an argument with TEXT, value type TYPE, and value VAL.
+ Add the argument to the argument list of CMD. */
+static struct arg *
+add_arg (struct cmd *cmd, char *text, int type, int val)
+{
+ struct arg *arg;
+
+ arg = boot_script_malloc (sizeof (struct arg));
+ if (arg)
+ {
+ arg->text = text;
+ arg->type = type;
+ arg->val = val;
+ if (add_list (arg, (void ***) &cmd->args,
+ &cmd->args_alloc, &cmd->args_index, 5))
+ {
+ boot_script_free (arg, sizeof *arg);
+ return 0;
+ }
+ }
+ return arg;
+}
+
+/* Search for the symbol NAME in the symbol table. */
+static struct sym *
+sym_lookup (const char *name)
+{
+ int i;
+
+ for (i = 0; i < symtab_index; i++)
+ if (! strcmp (name, symtab[i]->name))
+ return symtab[i];
+ return 0;
+}
+
+/* Create an entry for symbol NAME in the symbol table. */
+static struct sym *
+sym_enter (const char *name)
+{
+ struct sym *sym;
+
+ sym = boot_script_malloc (sizeof (struct sym));
+ if (sym)
+ {
+ memset (sym, 0, sizeof (struct sym));
+ sym->name = name;
+ if (add_list (sym, (void ***) &symtab, &symtab_alloc, &symtab_index, 20))
+ {
+ boot_script_free (sym, sizeof *sym);
+ return 0;
+ }
+ }
+ return sym;
+}
+
+/* Parse the command line CMDLINE. */
+int
+boot_script_parse_line (void *hook, char *cmdline)
+{
+ char *p, *q;
+ int error;
+ struct cmd *cmd;
+ struct arg *arg;
+
+ /* Extract command name. Ignore line if it lacks a command. */
+ for (p = cmdline; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == '#')
+ /* Ignore comment line. */
+ return 0;
+
+#if 0
+ if (*p && *p != ' ' && *p != '\t' && *p != '\n')
+ {
+ printf ("(bootstrap): %s\n", cmdline);
+ }
+#endif
+
+ for (q = p; *q && *q != ' ' && *q != '\t' && *q != '\n'; q++)
+ ;
+ if (p == q)
+ return 0;
+
+ *q = '\0';
+
+ /* Allocate a command structure. */
+ cmd = boot_script_malloc (sizeof (struct cmd));
+ if (! cmd)
+ return BOOT_SCRIPT_NOMEM;
+ memset (cmd, 0, sizeof (struct cmd));
+ cmd->hook = hook;
+ cmd->path = p;
+ p = q + 1;
+
+ for (arg = 0;;)
+ {
+ if (! arg)
+ {
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* End of command line. */
+ if (! *p || *p == '\n')
+ {
+ /* Add command to list. */
+ if (add_list (cmd, (void ***) &cmds,
+ &cmds_alloc, &cmds_index, 10))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ return 0;
+ }
+ }
+
+ /* Look for a symbol. */
+ if (arg || (*p == '$' && (*(p + 1) == '{' || *(p + 1) == '(')))
+ {
+ char end_char = (*(p + 1) == '{') ? '}' : ')';
+ struct sym *sym = 0;
+
+ for (p += 2;;)
+ {
+ char c;
+ int i, val, type;
+ struct sym *s;
+
+ /* Parse symbol name. */
+ for (q = p; *q && *q != '\n' && *q != end_char && *q != '='; q++)
+ ;
+ if (p == q || ! *q || *q == '\n'
+ || (end_char == '}' && *q != '}'))
+ {
+ error = BOOT_SCRIPT_SYNTAX_ERROR;
+ goto bad;
+ }
+ c = *q;
+ *q = '\0';
+
+ /* See if this is a builtin symbol. */
+ for (i = 0; i < NUM_BUILTIN; i++)
+ if (! strcmp (p, builtin_symbols[i].name))
+ break;
+
+ if (i < NUM_BUILTIN)
+ s = &builtin_symbols[i];
+ else
+ {
+ /* Look up symbol in symbol table.
+ If no entry exists, create one. */
+ s = sym_lookup (p);
+ if (! s)
+ {
+ s = sym_enter (p);
+ if (! s)
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ }
+ }
+
+ /* Only values are allowed in ${...} constructs. */
+ if (end_char == '}' && s->type == VAL_FUNC)
+ return BOOT_SCRIPT_INVALID_SYM;
+
+ /* Check that assignment is valid. */
+ if (c == '=' && s->type == VAL_FUNC)
+ {
+ error = BOOT_SCRIPT_INVALID_ASG;
+ goto bad;
+ }
+
+ /* For function symbols, execute the function. */
+ if (s->type == VAL_FUNC)
+ {
+ if (! s->run_on_exec)
+ {
+ (error
+ = ((*((int (*) (struct cmd *, int *)) s->val))
+ (cmd, &val)));
+ if (error)
+ goto bad;
+ type = s->ret_type;
+ }
+ else
+ {
+ if (add_list (s, (void ***) &cmd->exec_funcs,
+ &cmd->exec_funcs_alloc,
+ &cmd->exec_funcs_index, 5))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ type = VAL_NONE;
+ goto out;
+ }
+ }
+ else if (s->type == VAL_NONE)
+ {
+ type = VAL_SYM;
+ val = (int) s;
+ }
+ else
+ {
+ type = s->type;
+ val = s->val;
+ }
+
+ if (sym)
+ {
+ sym->type = type;
+ sym->val = val;
+ }
+ else if (arg)
+ {
+ arg->type = type;
+ arg->val = val;
+ }
+
+ out:
+ p = q + 1;
+ if (c == end_char)
+ {
+ /* Create an argument if necessary.
+ We create an argument if the symbol appears
+ in the expression by itself.
+
+ NOTE: This is temporary till the boot filesystem
+ servers support arguments. When that happens,
+ symbol values will only be printed if they're
+ associated with an argument. */
+ if (! arg && end_char == '}')
+ {
+ if (! add_arg (cmd, 0, type, val))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ }
+ arg = 0;
+ break;
+ }
+ if (s->type != VAL_FUNC)
+ sym = s;
+ }
+ }
+ else
+ {
+ char c;
+
+ /* Command argument; just copy the text. */
+ for (q = p;; q++)
+ {
+ if (! *q || *q == ' ' || *q == '\t' || *q == '\n')
+ break;
+ if (*q == '$' && *(q + 1) == '{')
+ break;
+ }
+ c = *q;
+ *q = '\0';
+
+ /* Add argument to list. */
+ arg = add_arg (cmd, p, VAL_NONE, 0);
+ if (! arg)
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ if (c == '$')
+ p = q;
+ else
+ {
+ if (c)
+ p = q + 1;
+ else
+ p = q;
+ arg = 0;
+ }
+ }
+ }
+
+
+ bad:
+ free_cmd (cmd, 1);
+ cleanup (1);
+ return error;
+}
+
+/* Ensure that the command line buffer can accommodate LEN bytes of space. */
+#define CHECK_CMDLINE_LEN(len) \
+{ \
+ if (cmdline_alloc - cmdline_index < len) \
+ { \
+ char *ptr; \
+ int alloc, i; \
+ alloc = cmdline_alloc + len - (cmdline_alloc - cmdline_index) + 100; \
+ ptr = boot_script_malloc (alloc); \
+ if (! ptr) \
+ { \
+ error = BOOT_SCRIPT_NOMEM; \
+ goto done; \
+ } \
+ memcpy (ptr, cmdline, cmdline_index); \
+ for (i = 0; i < argc; ++i) \
+ argv[i] = ptr + (argv[i] - cmdline); \
+ boot_script_free (cmdline, cmdline_alloc); \
+ cmdline = ptr; \
+ cmdline_alloc = alloc; \
+ } \
+}
+
+/* Execute commands previously parsed. */
+int
+boot_script_exec ()
+{
+ int cmd_index;
+
+ for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
+ {
+ char **argv, *cmdline;
+ int i, argc, cmdline_alloc;
+ int cmdline_index, error, arg_index;
+ struct cmd *cmd = cmds[cmd_index];
+
+ /* Skip command if it doesn't have an associated task. */
+ if (cmd->task == 0)
+ continue;
+
+ /* Allocate a command line and copy command name. */
+ cmdline_index = strlen (cmd->path) + 1;
+ cmdline_alloc = cmdline_index + 100;
+ cmdline = boot_script_malloc (cmdline_alloc);
+ if (! cmdline)
+ {
+ cleanup (1);
+ return BOOT_SCRIPT_NOMEM;
+ }
+ memcpy (cmdline, cmd->path, cmdline_index);
+
+ /* Allocate argument vector. */
+ argv = boot_script_malloc (sizeof (char *) * (cmd->args_index + 2));
+ if (! argv)
+ {
+ boot_script_free (cmdline, cmdline_alloc);
+ cleanup (1);
+ return BOOT_SCRIPT_NOMEM;
+ }
+ argv[0] = cmdline;
+ argc = 1;
+
+ /* Build arguments. */
+ for (arg_index = 0; arg_index < cmd->args_index; arg_index++)
+ {
+ struct arg *arg = cmd->args[arg_index];
+
+ /* Copy argument text. */
+ if (arg->text)
+ {
+ int len = strlen (arg->text);
+
+ if (arg->type == VAL_NONE)
+ len++;
+ CHECK_CMDLINE_LEN (len);
+ memcpy (cmdline + cmdline_index, arg->text, len);
+ argv[argc++] = &cmdline[cmdline_index];
+ cmdline_index += len;
+ }
+
+ /* Add value of any symbol associated with this argument. */
+ if (arg->type != VAL_NONE)
+ {
+ char *p, buf[50];
+ int len;
+ mach_port_t name;
+
+ if (arg->type == VAL_SYM)
+ {
+ struct sym *sym = (struct sym *) arg->val;
+
+ /* Resolve symbol value. */
+ while (sym->type == VAL_SYM)
+ sym = (struct sym *) sym->val;
+ if (sym->type == VAL_NONE)
+ {
+ error = BOOT_SCRIPT_UNDEF_SYM;
+ goto done;
+ }
+ arg->type = sym->type;
+ arg->val = sym->val;
+ }
+
+ /* Print argument value. */
+ switch (arg->type)
+ {
+ case VAL_STR:
+ p = (char *) arg->val;
+ len = strlen (p);
+ break;
+
+ case VAL_TASK:
+ case VAL_PORT:
+ if (arg->type == VAL_TASK)
+ /* Insert send right to task port. */
+ error = boot_script_insert_task_port
+ (cmd, (task_t) arg->val, &name);
+ else
+ /* Insert send right. */
+ error = boot_script_insert_right (cmd,
+ (mach_port_t) arg->val,
+ &name);
+ if (error)
+ goto done;
+
+ i = name;
+ p = buf + sizeof (buf);
+ len = 0;
+ do
+ {
+ *--p = i % 10 + '0';
+ len++;
+ }
+ while (i /= 10);
+ break;
+
+ default:
+ error = BOOT_SCRIPT_BAD_TYPE;
+ goto done;
+ }
+ len++;
+ CHECK_CMDLINE_LEN (len);
+ memcpy (cmdline + cmdline_index, p, len - 1);
+ *(cmdline + cmdline_index + len - 1) = '\0';
+ if (! arg->text)
+ argv[argc++] = &cmdline[cmdline_index];
+ cmdline_index += len;
+ }
+ }
+
+ /* Terminate argument vector. */
+ argv[argc] = 0;
+
+ /* Execute the command. */
+ if (boot_script_exec_cmd (cmd->hook, cmd->task, cmd->path,
+ argc, argv, cmdline, cmdline_index))
+ {
+ error = BOOT_SCRIPT_EXEC_ERROR;
+ goto done;
+ }
+
+ error = 0;
+
+ done:
+ boot_script_free (cmdline, cmdline_alloc);
+ boot_script_free (argv, sizeof (char *) * (cmd->args_index + 2));
+ if (error)
+ {
+ cleanup (1);
+ return error;
+ }
+ }
+
+ for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
+ {
+ int i;
+ struct cmd *cmd = cmds[cmd_index];
+
+ /* Execute functions that want to be run on exec. */
+ for (i = 0; i < cmd->exec_funcs_index; i++)
+ {
+ struct sym *sym = cmd->exec_funcs[i];
+ int error = ((*((int (*) (struct cmd *, int *)) sym->val))
+ (cmd, 0));
+ if (error)
+ {
+ cleanup (1);
+ return error;
+ }
+ }
+ }
+
+ cleanup (0);
+ return 0;
+}
+
+/* Create an entry for the variable NAME with TYPE and value VAL,
+ in the symbol table. */
+int
+boot_script_set_variable (const char *name, int type, int val)
+{
+ struct sym *sym = sym_enter (name);
+
+ if (sym)
+ {
+ sym->type = type;
+ sym->val = val;
+ }
+ return sym ? 0 : 1;
+}
+
+
+/* Define the function NAME, which will return type RET_TYPE. */
+int
+boot_script_define_function (const char *name, int ret_type,
+ int (*func) (const struct cmd *cmd, int *val))
+{
+ struct sym *sym = sym_enter (name);
+
+ if (sym)
+ {
+ sym->type = VAL_FUNC;
+ sym->val = (int) func;
+ sym->ret_type = ret_type;
+ sym->run_on_exec = ret_type == VAL_NONE;
+ }
+ return sym ? 0 : 1;
+}
+
+
+/* Return a string describing ERR. */
+char *
+boot_script_error_string (int err)
+{
+ switch (err)
+ {
+ case BOOT_SCRIPT_NOMEM:
+ return "no memory";
+
+ case BOOT_SCRIPT_SYNTAX_ERROR:
+ return "syntax error";
+
+ case BOOT_SCRIPT_INVALID_ASG:
+ return "invalid variable in assignment";
+
+ case BOOT_SCRIPT_MACH_ERROR:
+ return "mach error";
+
+ case BOOT_SCRIPT_UNDEF_SYM:
+ return "undefined symbol";
+
+ case BOOT_SCRIPT_EXEC_ERROR:
+ return "exec error";
+
+ case BOOT_SCRIPT_INVALID_SYM:
+ return "invalid variable in expression";
+
+ case BOOT_SCRIPT_BAD_TYPE:
+ return "invalid value type";
+ }
+ return 0;
+}
+
+#ifdef BOOT_SCRIPT_TEST
+#include <stdio.h>
+
+int
+boot_script_exec_cmd (void *hook,
+ mach_port_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen)
+{
+ int i;
+
+ printf ("port = %d: ", (int) task);
+ for (i = 0; i < argc; i++)
+ printf ("%s ", argv[i]);
+ printf ("\n");
+ return 0;
+}
+
+void
+main (int argc, char **argv)
+{
+ char buf[500], *p;
+ int len;
+ FILE *fp;
+ mach_port_t host_port, device_port;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: %s <script>\n", argv[0]);
+ exit (1);
+ }
+ fp = fopen (argv[1], "r");
+ if (! fp)
+ {
+ fprintf (stderr, "Can't open %s\n", argv[1]);
+ exit (1);
+ }
+ host_port = 1;
+ device_port = 2;
+ boot_script_set_variable ("host-port", VAL_PORT, (int) host_port);
+ boot_script_set_variable ("device-port", VAL_PORT, (int) device_port);
+ boot_script_set_variable ("root-device", VAL_STR, (int) "hd0a");
+ boot_script_set_variable ("boot-args", VAL_STR, (int) "-ad");
+ p = buf;
+ len = sizeof (buf);
+ while (fgets (p, len, fp))
+ {
+ int i, err;
+
+ i = strlen (p) + 1;
+ err = boot_script_parse_line (0, p);
+ if (err)
+ {
+ fprintf (stderr, "error %s\n", boot_script_error_string (err));
+ exit (1);
+ }
+ p += i;
+ len -= i;
+ }
+ boot_script_exec ();
+ exit (0);
+}
+#endif /* BOOT_SCRIPT_TEST */
diff --git a/kern/boot_script.h b/kern/boot_script.h
new file mode 100644
index 0000000..c436ac2
--- /dev/null
+++ b/kern/boot_script.h
@@ -0,0 +1,115 @@
+/* Definitions for boot script parser for Mach. */
+
+#ifndef _boot_script_h
+#define _boot_script_h
+
+/* Written by Shantanu Goel (goel@cs.columbia.edu). */
+
+/* Error codes returned by boot_script_parse_line()
+ and boot_script_exec_cmd(). */
+#define BOOT_SCRIPT_NOMEM 1
+#define BOOT_SCRIPT_SYNTAX_ERROR 2
+#define BOOT_SCRIPT_INVALID_ASG 3
+#define BOOT_SCRIPT_MACH_ERROR 4
+#define BOOT_SCRIPT_UNDEF_SYM 5
+#define BOOT_SCRIPT_EXEC_ERROR 6
+#define BOOT_SCRIPT_INVALID_SYM 7
+#define BOOT_SCRIPT_BAD_TYPE 8
+
+/* Legal values for argument `type' to function
+ boot_script_set_variable and boot_script_define_function. */
+#define VAL_NONE 0 /* none -- function runs at exec time */
+#define VAL_STR 1 /* string */
+#define VAL_PORT 2 /* port */
+#define VAL_TASK 3 /* task port */
+
+/* This structure describes a command. */
+struct cmd
+{
+ /* Cookie passed in to boot_script_parse_line. */
+ void *hook;
+
+ /* Path of executable. */
+ char *path;
+
+ /* Task port. */
+ task_t task;
+
+ /* Argument list. */
+ struct arg **args;
+
+ /* Amount allocated for `args'. */
+ int args_alloc;
+
+ /* Next available slot in `args'. */
+ int args_index;
+
+ /* List of functions that want to be run on command execution. */
+ struct sym **exec_funcs;
+
+ /* Amount allocated for `exec_funcs'. */
+ int exec_funcs_alloc;
+
+ /* Next available slot in `exec_funcs'. */
+ int exec_funcs_index;
+};
+
+
+/* The user must define these functions, we work like malloc and free. */
+void *boot_script_malloc (unsigned int);
+void boot_script_free (void *, unsigned int);
+
+/* The user must define this function. Load the image of the
+ executable specified by PATH in TASK. Create a thread
+ in TASK and point it at the executable's entry point. Initialize
+ TASK's stack with argument vector ARGV of length ARGC whose
+ strings are STRINGS. STRINGS has length STRINGLEN.
+ Return 0 for success, non-zero otherwise. */
+int boot_script_exec_cmd (void *hook,
+ task_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen);
+
+/* The user must define this function. Load the contents of FILE
+ into a fresh anonymous memory object and return the memory object port. */
+mach_port_t boot_script_read_file (const char *file);
+
+/* The user must define this functions to perform the corresponding
+ Mach task manipulations. */
+int boot_script_task_create (struct cmd *); /* task_create + task_suspend */
+int boot_script_task_resume (struct cmd *);
+int boot_script_prompt_task_resume (struct cmd *);
+int boot_script_insert_right (struct cmd *, mach_port_t, mach_port_t *namep);
+int boot_script_insert_task_port (struct cmd *, task_t, mach_port_t *namep);
+
+/* The user must define this function to clean up the `task_t'
+ returned by boot_script_task_create. */
+void boot_script_free_task (task_t task, int aborting);
+
+
+/* Parse the command line LINE. This causes the command line to be
+ converted into an internal format. Returns 0 for success, non-zero
+ otherwise.
+
+ NOTE: The parser writes into the line so it must not be a string constant.
+ It is also the responsibility of the caller not to deallocate the line
+ across calls to the parser. */
+int boot_script_parse_line (void *hook, char *cmdline);
+
+/* Execute the command lines prevously parsed.
+ Returns 0 for success, non-zero otherwise. */
+int boot_script_exec (void);
+
+/* Create an entry in the symbol table for variable NAME,
+ whose type is TYPE and value is VAL. Returns 0 on success,
+ non-zero otherwise. */
+int boot_script_set_variable (const char *name, int type, int val);
+
+/* Define the function NAME, which will return type RET_TYPE. */
+int boot_script_define_function (const char *name, int ret_type,
+ int (*func) (const struct cmd *cmd, int *val));
+
+/* Returns a string describing the error ERR. */
+char *boot_script_error_string (int err);
+
+
+#endif /* _boot_script_h */
diff --git a/kern/bootstrap.c b/kern/bootstrap.c
index 39a039d..3b2705b 100644
--- a/kern/bootstrap.c
+++ b/kern/bootstrap.c
@@ -37,31 +37,43 @@
#include "vm_param.h"
#include <ipc/ipc_port.h>
#include <kern/host.h>
-#include <kern/strings.h>
#include <kern/task.h>
#include <kern/thread.h>
+#include <kern/lock.h>
#include <vm/vm_kern.h>
#include <device/device_port.h>
-#include <stdarg.h>
-
-#include <mach/machine/multiboot.h>
-#include <mach/exec/exec.h>
-
#if MACH_KDB
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#endif
+#if OSKIT_MACH
+#include <stddef.h>
+#include <string.h>
+#include <oskit/machine/base_multiboot.h>
+#include <oskit/exec/exec.h>
+#include <oskit/c/stdio.h>
+#define safe_gets(s, n) fgets((s),(n),stdin)
+#else
+#include <mach/machine/multiboot.h>
+#include <mach/exec/exec.h>
+#include <kern/strings.h>
+extern struct multiboot_info boot_info; /* XXX put this in a header! */
+#endif
+
+#include "boot_script.h"
+
static mach_port_t boot_device_port; /* local name */
static mach_port_t boot_host_port; /* local name */
-extern struct multiboot_info boot_info;
extern char *kernel_cmdline;
static void user_bootstrap(); /* forward */
-static void bootstrap_exec(void *exec_data);
+static void user_bootstrap_compat(); /* forward */
+static void bootstrap_exec_compat(void *exec_data); /* forward */
+static void get_compat_strings(char *flags_str, char *root_str); /* forward */
static mach_port_t
task_insert_send_right(
@@ -85,25 +97,113 @@ task_insert_send_right(
void bootstrap_create()
{
- struct multiboot_module *bmod;
-
- if (!(boot_info.flags & MULTIBOOT_MODS)
- || (boot_info.mods_count == 0))
- panic("No bootstrap code loaded with the kernel!");
- if (boot_info.mods_count > 1)
- printf("Warning: only one boot module currently used by Mach\n");
- bmod = (struct multiboot_module *)phystokv(boot_info.mods_addr);
- bootstrap_exec((void*)phystokv(bmod->mod_start));
-
- /* XXX at this point, we could free all the memory used
- by the boot modules and the boot loader's descriptors and such. */
-}
+ struct multiboot_module *bmods = ((struct multiboot_module *)
+ phystokv(boot_info.mods_addr));
+
+ if (!(boot_info.flags & MULTIBOOT_MODS)
+ || (boot_info.mods_count == 0))
+ panic ("No bootstrap code loaded with the kernel!");
+
+ if (boot_info.mods_count == 1
+ && strchr((char*)phystokv(bmods[0].string), ' ') == 0)
+ {
+ printf("Loading single multiboot module in compat mode: %s\n",
+ (char*)phystokv(bmods[0].string));
+ bootstrap_exec_compat(&bmods[0]);
+ }
+ else
+ {
+ int i, losers;
+
+ /* Initialize boot script variables. We leak these send rights. */
+ losers = boot_script_set_variable
+ ("host-port", VAL_PORT,
+ (int)ipc_port_make_send(realhost.host_priv_self));
+ if (losers)
+ panic ("cannot set boot-script variable host-port: %s",
+ boot_script_error_string (losers));
+ losers = boot_script_set_variable
+ ("device-port", VAL_PORT,
+ (int) ipc_port_make_send(master_device_port));
+ if (losers)
+ panic ("cannot set boot-script variable device-port: %s",
+ boot_script_error_string (losers));
+
+ losers = boot_script_set_variable ("kernel-command-line", VAL_STR,
+ (int) kernel_cmdline);
+ if (losers)
+ panic ("cannot set boot-script variable %s: %s",
+ "kernel-command-line", boot_script_error_string (losers));
+
+#if OSKIT_MACH
+ {
+ /* The oskit's "environ" array contains all the words from
+ the multiboot command line that looked like VAR=VAL.
+ We set each of these as boot-script variables, which
+ can be used for things like ${root}. */
+
+ extern char **environ;
+ char **ep;
+ for (ep = environ; *ep != 0; ++ep)
+ {
+ size_t len = strlen (*ep) + 1;
+ char *var = memcpy (alloca (len), *ep, len);
+ char *val = strchr (var, '=');
+ *val++ = '\0';
+ losers = boot_script_set_variable (var, VAL_STR, (int) val);
+ if (losers)
+ panic ("cannot set boot-script variable %s: %s",
+ var, boot_script_error_string (losers));
+ }
+ }
+#else /* GNUmach, not oskit-mach */
+ {
+ char *flag_string = alloca(1024);
+ char *root_string = alloca(1024);
+
+ /*
+ * Get the (compatibility) boot flags and root name strings.
+ */
+ get_compat_strings(flag_string, root_string);
+
+ losers = boot_script_set_variable ("boot-args", VAL_STR,
+ (int) flag_string);
+ if (losers)
+ panic ("cannot set boot-script variable %s: %s",
+ "boot-args", boot_script_error_string (losers));
+ losers = boot_script_set_variable ("root-device", VAL_STR,
+ (int) root_string);
+ if (losers)
+ panic ("cannot set boot-script variable %s: %s",
+ "root-device", boot_script_error_string (losers));
+ }
+#endif
-/* XXX won't work with more than one bootstrap service */
-static void *boot_exec;
+ for (i = 0; i < boot_info.mods_count; ++i)
+ {
+ char *line = (char*)phystokv(bmods[i].string);
+ int err = boot_script_parse_line (&bmods[i], line);
+ if (err)
+ {
+ printf ("ERROR: %s in multiboot module string: %s\n",
+ boot_script_error_string (err), line);
+ ++losers;
+ }
+ }
+ if (losers)
+ panic ("%d of %d boot script commands could not be parsed",
+ losers, boot_info.mods_count);
+ losers = boot_script_exec ();
+ if (losers)
+ panic ("ERROR in executing boot script: %s",
+ boot_script_error_string (losers));
+ }
+ /* XXX at this point, we could free all the memory used
+ by the boot modules and the boot loader's descriptors and such. */
+}
static void
-bootstrap_exec(void *e)
+bootstrap_exec_compat(void *e)
{
task_t bootstrap_task;
thread_t bootstrap_thread;
@@ -130,8 +230,8 @@ bootstrap_exec(void *e)
/*
* Start the bootstrap thread.
*/
- boot_exec = e;
- thread_start(bootstrap_thread, user_bootstrap);
+ bootstrap_thread->saved.other = e;
+ thread_start(bootstrap_thread, user_bootstrap_compat);
(void) thread_resume(bootstrap_thread);
}
@@ -171,6 +271,8 @@ static void get_compat_strings(char *flags_str, char *root_str)
{
register char *ip, *cp;
+ strcpy (root_str, "UNKNOWN");
+
cp = flags_str;
*cp++ = '-';
@@ -237,23 +339,35 @@ static boolean_t load_bootstrap_symbols = FALSE;
-static int boot_read(void *handle, vm_offset_t file_ofs, void *buf, vm_size_t size,
- vm_size_t *out_actual)
+static int
+boot_read(void *handle, vm_offset_t file_ofs, void *buf, vm_size_t size,
+ vm_size_t *out_actual)
{
- memcpy(buf, handle + file_ofs, size);
- *out_actual = size;
- return 0;
+ struct multiboot_module *mod = handle;
+
+ if (mod->mod_start + file_ofs + size > mod->mod_end)
+ return -1;
+
+ memcpy(buf, (const char*) phystokv (mod->mod_start) + file_ofs, size);
+ *out_actual = size;
+ return 0;
}
-static int read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
+static int
+read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
vm_offset_t mem_addr, vm_size_t mem_size,
exec_sectype_t sec_type)
{
+ struct multiboot_module *mod = handle;
+
vm_map_t user_map = current_task()->map;
vm_offset_t start_page, end_page;
vm_prot_t mem_prot = sec_type & EXEC_SECTYPE_PROT_MASK;
int err;
+ if (mod->mod_start + file_ofs + file_size > mod->mod_end)
+ return -1;
+
if (!(sec_type & EXEC_SECTYPE_ALLOC))
return 0;
@@ -263,10 +377,10 @@ static int read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
start_page = trunc_page(mem_addr);
end_page = round_page(mem_addr + mem_size);
- /*
+#if 0
printf("reading bootstrap section %08x-%08x-%08x prot %d pages %08x-%08x\n",
mem_addr, mem_addr+file_size, mem_addr+mem_size, mem_prot, start_page, end_page);
- */
+#endif
err = vm_allocate(user_map, &start_page, end_page - start_page, FALSE);
assert(err == 0);
@@ -274,7 +388,8 @@ static int read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
if (file_size > 0)
{
- err = copyout(handle + file_ofs, mem_addr, file_size);
+ err = copyout((char *)phystokv (mod->mod_start) + file_ofs,
+ mem_addr, file_size);
assert(err == 0);
}
@@ -283,9 +398,11 @@ static int read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
err = vm_protect(user_map, start_page, end_page - start_page, FALSE, mem_prot);
assert(err == 0);
}
+
+ return 0;
}
-static void copy_bootstrap(void *e, struct exec_info *boot_exec_info)
+static void copy_bootstrap(void *e, exec_info_t *boot_exec_info)
{
register vm_map_t user_map = current_task()->map;
int err;
@@ -334,53 +451,49 @@ static void copy_bootstrap(void *e, struct exec_info *boot_exec_info)
extern vm_offset_t user_stack_low();
extern vm_offset_t set_user_regs();
-void
-static build_args_and_stack(struct exec_info *boot_exec_info, ...)
+static void
+build_args_and_stack(struct exec_info *boot_exec_info,
+ char **argv, char **envp)
{
vm_offset_t stack_base;
vm_size_t stack_size;
- va_list argv_ptr;
register
char * arg_ptr;
+ int arg_count, envc;
int arg_len;
- int arg_count;
- register
char * arg_pos;
int arg_item_len;
char * string_pos;
char * zero = (char *)0;
- static const char cmdline_var[] = "MULTIBOOT_CMDLINE=";
+ int i;
#define STACK_SIZE (64*1024)
/*
* Calculate the size of the argument list.
*/
- va_start(argv_ptr, boot_exec_info);
arg_len = 0;
arg_count = 0;
- for (;;) {
- arg_ptr = va_arg(argv_ptr, char *);
- if (arg_ptr == 0)
- break;
- arg_count++;
+ while (argv[arg_count] != 0) {
+ arg_ptr = argv[arg_count++];
arg_len += strlen(arg_ptr) + 1;
}
- va_end(argv_ptr);
-
- if (kernel_cmdline[0] != '\0')
- arg_len += sizeof cmdline_var + strlen (kernel_cmdline);
+ envc = 0;
+ if (envp != 0)
+ while (envp[envc] != 0)
+ arg_len += strlen (envp[envc++]) + 1;
/*
* Add space for:
* arg count
* pointers to arguments
* trailing 0 pointer
- * dummy 0 pointer to environment variables
+ * pointers to environment variables
+ * trailing 0 pointer
* and align to integer boundary
*/
- arg_len += sizeof(integer_t)
- + (3 + arg_count) * sizeof(char *);
+ arg_len += (sizeof(integer_t)
+ + (arg_count + 1 + envc + 1) * sizeof(char *));
arg_len = (arg_len + sizeof(integer_t) - 1) & ~(sizeof(integer_t)-1);
/*
@@ -399,10 +512,9 @@ static build_args_and_stack(struct exec_info *boot_exec_info, ...)
/*
* Start the strings after the arg-count and pointers
*/
- string_pos = arg_pos
- + sizeof(integer_t)
- + arg_count * sizeof(char *)
- + 3 * sizeof(char *);
+ string_pos = (arg_pos
+ + sizeof(integer_t)
+ + (arg_count + 1 + envc + 1) * sizeof(char *));
/*
* first the argument count
@@ -415,9 +527,8 @@ static build_args_and_stack(struct exec_info *boot_exec_info, ...)
/*
* Then the strings and string pointers for each argument
*/
- va_start(argv_ptr, boot_exec_info);
- while (--arg_count >= 0) {
- arg_ptr = va_arg(argv_ptr, char *);
+ for (i = 0; i < arg_count; ++i) {
+ arg_ptr = argv[i];
arg_item_len = strlen(arg_ptr) + 1; /* include trailing 0 */
/* set string pointer */
@@ -430,7 +541,6 @@ static build_args_and_stack(struct exec_info *boot_exec_info, ...)
(void) copyout(arg_ptr, string_pos, arg_item_len);
string_pos += arg_item_len;
}
- va_end(argv_ptr);
/*
* Null terminator for argv.
@@ -439,9 +549,12 @@ static build_args_and_stack(struct exec_info *boot_exec_info, ...)
arg_pos += sizeof(char *);
/*
- * If we have a command line, put it in an environment variable.
+ * Then the strings and string pointers for each environment variable
*/
- if (kernel_cmdline[0] != '\0') {
+ for (i = 0; i < envc; ++i) {
+ arg_ptr = envp[i];
+ arg_item_len = strlen(arg_ptr) + 1; /* include trailing 0 */
+
/* set string pointer */
(void) copyout((char *)&string_pos,
arg_pos,
@@ -449,36 +562,31 @@ static build_args_and_stack(struct exec_info *boot_exec_info, ...)
arg_pos += sizeof(char *);
/* copy string */
- arg_ptr = (char *) cmdline_var;
- arg_item_len = sizeof cmdline_var - 1;
- (void) copyout(arg_ptr, string_pos, arg_item_len);
- string_pos += arg_item_len;
-
- /* copy string */
- arg_ptr = kernel_cmdline;
- arg_item_len = strlen(kernel_cmdline) + 1;
(void) copyout(arg_ptr, string_pos, arg_item_len);
string_pos += arg_item_len;
}
+
/*
* Null terminator for envp.
*/
(void) copyout((char *)&zero, arg_pos, sizeof(char *));
}
-static void user_bootstrap()
+
+static void
+user_bootstrap_compat()
{
- struct exec_info boot_exec_info;
+ exec_info_t boot_exec_info;
char host_string[12];
char device_string[12];
- char flag_string[12];
- char root_string[12];
+ char flag_string[1024];
+ char root_string[1024];
/*
* Copy the bootstrap code from boot_exec into the user task.
*/
- copy_bootstrap(boot_exec, &boot_exec_info);
+ copy_bootstrap(current_thread()->saved.other, &boot_exec_info);
/*
* Convert the host and device ports to strings,
@@ -496,14 +604,27 @@ static void user_bootstrap()
* Build the argument list and insert in the user task.
* Argument list is
* "bootstrap -<boothowto> <host_port> <device_port> <root_name>"
+
+$0 ${boot-args} ${host-port} ${device-port} ${root-device} $(task-create) $(task-resume)
+
*/
- build_args_and_stack(&boot_exec_info,
- "bootstrap",
- flag_string,
- host_string,
- device_string,
- root_string,
- (char *)0);
+ {
+ char *argv[] = { "bootstrap",
+ flag_string,
+ host_string,
+ device_string,
+ root_string,
+ 0 };
+ char *envp[] = { 0, 0 };
+ if (kernel_cmdline[0] != '\0')
+ {
+ static const char cmdline_var[] = "MULTIBOOT_CMDLINE=";
+ envp[0] = alloca (sizeof cmdline_var + strlen (kernel_cmdline));
+ memcpy (envp[0], cmdline_var, sizeof cmdline_var - 1);
+ strcpy (envp[0] + sizeof cmdline_var - 1, kernel_cmdline);
+ }
+ build_args_and_stack(&boot_exec_info, argv, envp);
+ }
/*
* Exit to user thread.
@@ -511,3 +632,148 @@ static void user_bootstrap()
thread_bootstrap_return();
/*NOTREACHED*/
}
+
+
+struct user_bootstrap_info
+{
+ struct multiboot_module *mod;
+ char **argv;
+ int done;
+ decl_simple_lock_data(,lock)
+};
+
+int
+boot_script_exec_cmd (void *hook, task_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen)
+{
+ struct multiboot_module *mod = hook;
+ exec_info_t boot_exec_info;
+
+ register vm_map_t user_map = current_task()->map;
+ int err;
+
+ if (task != MACH_PORT_NULL)
+ {
+ thread_t thread;
+ struct user_bootstrap_info info = { mod, argv, 0, };
+ simple_lock_init (&info.lock);
+ simple_lock (&info.lock);
+
+ err = thread_create ((task_t)task, &thread);
+ assert(err == 0);
+ thread->saved.other = &info;
+ thread_start (thread, user_bootstrap);
+ thread_resume (thread);
+
+ /* We need to synchronize with the new thread and block this
+ main thread until it has finished referring to our local state. */
+ while (! info.done)
+ {
+ thread_sleep ((event_t) &info, simple_lock_addr(info.lock), FALSE);
+ simple_lock (&info.lock);
+ }
+ }
+
+ return 0;
+}
+
+static void user_bootstrap()
+{
+ struct user_bootstrap_info *info = current_thread()->saved.other;
+ exec_info_t boot_exec_info;
+ int err;
+
+ /* Load this task up from the executable file in the module. */
+ err = exec_load(boot_read, read_exec, info->mod, &boot_exec_info);
+ if (err)
+ panic ("Cannot load user executable module (error code %d): %s",
+ err, info->argv[0]);
+
+ /* Set up the stack with arguments. */
+ build_args_and_stack(&boot_exec_info, info->argv, 0);
+
+ task_suspend (current_task());
+
+ /* Tell the bootstrap thread running boot_script_exec_cmd
+ that we are done looking at INFO. */
+ simple_lock (&info->lock);
+ assert (!info->done);
+ info->done = 1;
+ thread_wakeup ((event_t) info);
+
+ /*
+ * Exit to user thread.
+ */
+ thread_bootstrap_return();
+ /*NOTREACHED*/
+}
+
+
+
+void *
+boot_script_malloc (unsigned int size)
+{
+ return (void *) kalloc (size);
+}
+
+void
+boot_script_free (void *ptr, unsigned int size)
+{
+ kfree ((vm_offset_t)ptr, size);
+}
+
+int
+boot_script_task_create (struct cmd *cmd)
+{
+ kern_return_t rc = task_create(TASK_NULL, FALSE, &cmd->task);
+ if (rc)
+ {
+ printf("boot_script_task_create failed with %x\n", rc);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ return 0;
+}
+
+int
+boot_script_task_resume (struct cmd *cmd)
+{
+ kern_return_t rc = task_resume (cmd->task);
+ if (rc)
+ {
+ printf("boot_script_task_resume failed with %x\n", rc);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ return 0;
+}
+
+int
+boot_script_prompt_task_resume (struct cmd *cmd)
+{
+ char xx[5];
+
+ printf ("Hit return to resume %s...", cmd->path);
+ safe_gets (xx, sizeof xx);
+
+ return boot_script_task_resume (cmd);
+}
+
+void
+boot_script_free_task (task_t task, int aborting)
+{
+ if (aborting)
+ task_terminate (task);
+}
+
+int
+boot_script_insert_right (struct cmd *cmd, mach_port_t port, mach_port_t *name)
+{
+ *name = task_insert_send_right (cmd->task, (ipc_port_t)port);
+ return 0;
+}
+
+int
+boot_script_insert_task_port (struct cmd *cmd, task_t task, mach_port_t *name)
+{
+ *name = task_insert_send_right (cmd->task, task->itk_sself);
+ return 0;
+}