diff options
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | kern/boot_script.c | 788 | ||||
-rw-r--r-- | kern/boot_script.h | 115 | ||||
-rw-r--r-- | kern/bootstrap.c | 436 |
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 @@ -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; +} |