diff options
Diffstat (limited to 'libdiskfs/boot-start.c')
-rw-r--r-- | libdiskfs/boot-start.c | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c new file mode 100644 index 00000000..98889e6f --- /dev/null +++ b/libdiskfs/boot-start.c @@ -0,0 +1,662 @@ +/* + Copyright (C) 1993,94,95,96,97,98,99 Free Software Foundation, Inc. + +This file is part of the GNU Hurd. + +The GNU Hurd is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The GNU Hurd is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the GNU Hurd; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Michael I. Bushnell. */ + +#include "priv.h" +#include <stdio.h> +#include <hurd.h> +#include <hurd/fsys.h> +#include <hurd/exec.h> +#include <hurd/startup.h> +#include <hurd/paths.h> +#include <fcntl.h> +#include <device/device.h> +#include <sys/reboot.h> +#include <string.h> +#include <argz.h> +#include <error.h> +#include "fsys_S.h" +#include "fsys_reply_U.h" + +static mach_port_t diskfs_exec_ctl; +extern task_t diskfs_exec_server_task; +static task_t parent_task = MACH_PORT_NULL; + +static struct mutex execstartlock; +static struct condition execstarted; + +static char *default_init = "hurd/init"; + +static void start_execserver (); + +char **diskfs_argv = 0; + +static mach_port_t +get_console () +{ + mach_port_t device_master, console; + error_t err = get_privileged_ports (0, &device_master); + + if (err) + return MACH_PORT_NULL; + + err = device_open (device_master, D_WRITE | D_READ, "console", &console); + if (err) + return MACH_PORT_NULL; + + return console; +} + +/* Make sure we have the privileged ports. */ +void +_diskfs_boot_privports (void) +{ + assert (diskfs_boot_flags); + if (_hurd_host_priv == MACH_PORT_NULL) + { + /* We are the boot command run by the real bootstrap filesystem. + We get the privileged ports from it as init would. */ + mach_port_t bootstrap; + error_t err = task_get_bootstrap_port (mach_task_self (), &bootstrap); + assert_perror (err); + err = fsys_getpriv (bootstrap, &_hurd_host_priv, &_hurd_device_master, + &parent_task); + mach_port_deallocate (mach_task_self (), bootstrap); + assert_perror (err); + } +} + +/* Once diskfs_root_node is set, call this if we are a bootstrap + filesystem. */ +void +diskfs_start_bootstrap () +{ + mach_port_t root_pt, startup_pt, bootpt; + retry_type retry; + char pathbuf[1024]; + string_t retry_name; + mach_port_t portarray[INIT_PORT_MAX]; + mach_port_t fdarray[3]; /* XXX */ + task_t newt; + error_t err; + char *exec_argv, *exec_env, *initname; + size_t exec_argvlen, exec_envlen; + struct port_info *bootinfo; + struct protid *rootpi; + mach_port_t diskfs_exec; + + /* Create the port for current and root directory. */ + err = diskfs_create_protid (diskfs_make_peropen (diskfs_root_node, + O_READ | O_EXEC, 0), + 0, &rootpi); + assert_perror (err); + root_pt = ports_get_right (rootpi); + + /* Get us a send right to copy around. */ + mach_port_insert_right (mach_task_self (), root_pt, root_pt, + MACH_MSG_TYPE_MAKE_SEND); + + ports_port_deref (rootpi); + + if (diskfs_exec_server_task == MACH_PORT_NULL) + { + /* We are the boot command run by the real bootstrap filesystem. + Our parent (the real bootstrap filesystem) provides us a root + directory where we look up /servers/exec like any non-bootstrap + filesystem would. */ + assert (_hurd_ports); + assert (_hurd_ports[INIT_PORT_CRDIR].port != MACH_PORT_NULL); + diskfs_exec = file_name_lookup (_SERVERS_EXEC, 0, 0); + if (diskfs_exec == MACH_PORT_NULL) + error (1, errno, "%s", _SERVERS_EXEC); + else + { +#ifndef NDEBUG + /* Make sure this is really a port to another server. */ + struct port_info *pi = ports_lookup_port (diskfs_port_bucket, + diskfs_exec, 0); + assert (!pi); +#endif + } + + /* Here we assume the parent has already printed: + Hurd server bootstrap: bootfs[bootdev] exec ourfs + */ + printf ("\nContinuing on new root filesystem %s:", diskfs_disk_name); + fflush (stdout); + } + else + { + uid_t idlist[] = {0, 0, 0}; + file_t execnode; + + printf ("Hurd server bootstrap: %s[%s]", + program_invocation_short_name, diskfs_disk_name); + fflush (stdout); + + /* Get the execserver going and wait for its fsys_startup */ + mutex_init (&execstartlock); + condition_init (&execstarted); + mutex_lock (&execstartlock); + start_execserver (); + condition_wait (&execstarted, &execstartlock); + mutex_unlock (&execstartlock); + assert (diskfs_exec_ctl != MACH_PORT_NULL); + + /* Contact the exec server. */ + err = fsys_getroot (diskfs_exec_ctl, root_pt, MACH_MSG_TYPE_COPY_SEND, + idlist, 3, idlist, 3, 0, + &retry, retry_name, &diskfs_exec); + assert_perror (err); + assert (retry == FS_RETRY_NORMAL); + assert (retry_name[0] == '\0'); + assert (diskfs_exec != MACH_PORT_NULL); + + /* Attempt to set the active translator for the exec server so that + filesystems other than the bootstrap can find it. */ + err = dir_lookup (root_pt, _SERVERS_EXEC, O_NOTRANS, 0, + &retry, pathbuf, &execnode); + if (err) + { + error (0, err, "cannot set translator on %s", _SERVERS_EXEC); + mach_port_deallocate (mach_task_self (), diskfs_exec_ctl); + } + else + { + assert (retry == FS_RETRY_NORMAL); + assert (retry_name[0] == '\0'); + assert (execnode != MACH_PORT_NULL); + err = file_set_translator (execnode, 0, FS_TRANS_SET, 0, 0, 0, + diskfs_exec_ctl, MACH_MSG_TYPE_MOVE_SEND); + mach_port_deallocate (mach_task_self (), execnode); + assert_perror (err); + } + diskfs_exec_ctl = MACH_PORT_NULL; /* Not used after this. */ + } + + if (_diskfs_boot_command) + { + /* We have a boot command line to run instead of init. */ + err = argz_create (_diskfs_boot_command, &exec_argv, &exec_argvlen); + assert_perror (err); + initname = exec_argv; + while (*initname == '/') + initname++; + } + else + { + /* Choose the name of the startup server to execute. */ + char *initnamebuf; + if (index (diskfs_boot_flags, 'i')) + { + size_t bufsz; + ssize_t len; + initname = default_init; + prompt: + initnamebuf = NULL; + printf ("\nInit name [%s]: ", initname); + fflush (stdout); + bufsz = 0; + switch (len = getline (&initnamebuf, &bufsz, stdin)) + { + case -1: + perror ("getline"); + printf ("Using default of `%s'.\n", initname); + case 0: /* Hmm. */ + case 1: /* Empty line, just a newline. */ + /* Use default. */ + break; + default: + initnamebuf[len - 1] = '\0'; /* Remove the newline. */ + initname = initnamebuf; + while (*initname == '/') + initname++; + break; + } + } + else + { + initname = default_init; + initnamebuf = NULL; + } + + exec_argvlen = asprintf (&exec_argv, "/%s%c%s%c", + initname, '\0', diskfs_boot_flags, '\0'); + if (initname != default_init) + free (initnamebuf); + initname = exec_argv + 1; + } + + err = dir_lookup (root_pt, initname, O_READ, 0, + &retry, pathbuf, &startup_pt); + if (err) + { + printf ("\nCannot find startup program `%s': %s\n", + initname, strerror (err)); + fflush (stdout); + free (exec_argv); + goto prompt; + } + assert (retry == FS_RETRY_NORMAL); + assert (pathbuf[0] == '\0'); + + err = ports_create_port (diskfs_initboot_class, diskfs_port_bucket, + sizeof (struct port_info), &bootinfo); + assert_perror (err); + bootpt = ports_get_right (bootinfo); + mach_port_insert_right (mach_task_self (), bootpt, bootpt, + MACH_MSG_TYPE_MAKE_SEND); + ports_port_deref (bootinfo); + + portarray[INIT_PORT_CRDIR] = root_pt; + portarray[INIT_PORT_CWDIR] = root_pt; + portarray[INIT_PORT_AUTH] = MACH_PORT_NULL; + portarray[INIT_PORT_PROC] = MACH_PORT_NULL; + portarray[INIT_PORT_CTTYID] = MACH_PORT_NULL; + portarray[INIT_PORT_BOOTSTRAP] = bootpt; + + fdarray[0] = fdarray[1] = fdarray[2] = get_console (); /* XXX */ + + err = argz_create (environ, &exec_env, &exec_envlen); + assert_perror (err); + + err = task_create (mach_task_self (), 0, &newt); + assert_perror (err); + if (index (diskfs_boot_flags, 'd')) + { + printf ("pausing for %s...\n", exec_argv); + getc (stdin); + } + printf (" %s", basename (exec_argv)); + fflush (stdout); + err = exec_exec (diskfs_exec, startup_pt, MACH_MSG_TYPE_COPY_SEND, + newt, 0, exec_argv, exec_argvlen, exec_env, exec_envlen, + fdarray, MACH_MSG_TYPE_COPY_SEND, 3, + portarray, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, + /* Supply no intarray, since we have no info for it. + With none supplied, it will use the defaults. */ + NULL, 0, 0, 0, 0, 0); + free (exec_argv); + free (exec_env); + mach_port_deallocate (mach_task_self (), root_pt); + mach_port_deallocate (mach_task_self (), startup_pt); + mach_port_deallocate (mach_task_self (), bootpt); + assert_perror (err); + + /* Cache the exec server port for file_exec to use. */ + _hurd_port_set (&_diskfs_exec_portcell, diskfs_exec); +} + +/* We look like an execserver to the execserver itself; it makes this + call (as does any task) to get its state. We can't give it all of + its ports (we'll provide those with a later call to exec_init). */ +kern_return_t +diskfs_S_exec_startup_get_info (mach_port_t port, + vm_address_t *user_entry, + vm_address_t *phdr_data, + vm_size_t *phdr_size, + vm_address_t *base_addr, + vm_size_t *stack_size, + int *flags, + char **argvP, + mach_msg_type_number_t *argvlen, + char **envpP __attribute__ ((unused)), + mach_msg_type_number_t *envplen, + mach_port_t **dtableP, + mach_msg_type_name_t *dtablepoly, + mach_msg_type_number_t *dtablelen, + mach_port_t **portarrayP, + mach_msg_type_name_t *portarraypoly, + mach_msg_type_number_t *portarraylen, + int **intarrayP, + mach_msg_type_number_t *intarraylen) +{ + error_t err; + mach_port_t *portarray, *dtable; + mach_port_t rootport; + struct ufsport *upt; + struct protid *rootpi; + + if (!(upt = ports_lookup_port (diskfs_port_bucket, port, + diskfs_execboot_class))) + return EOPNOTSUPP; + + *user_entry = 0; + *phdr_data = *base_addr = 0; + *phdr_size = *stack_size = 0; + + /* We have no args for it. Tell it to look on its stack + for the args placed there by the boot loader. */ + *argvlen = *envplen = 0; + *flags = EXEC_STACK_ARGS; + + if (*portarraylen < INIT_PORT_MAX) + *portarrayP = mmap (0, INIT_PORT_MAX * sizeof (mach_port_t), + PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + portarray = *portarrayP; + *portarraylen = INIT_PORT_MAX; + + if (*dtablelen < 3) + *dtableP = mmap (0, 3 * sizeof (mach_port_t), PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + dtable = *dtableP; + *dtablelen = 3; + dtable[0] = dtable[1] = dtable[2] = get_console (); /* XXX */ + + *intarrayP = NULL; + *intarraylen = 0; + + err = diskfs_create_protid (diskfs_make_peropen (diskfs_root_node, + O_READ | O_EXEC, 0), + 0, &rootpi); + assert_perror (err); + rootport = ports_get_right (rootpi); + ports_port_deref (rootpi); + portarray[INIT_PORT_CWDIR] = rootport; + portarray[INIT_PORT_CRDIR] = rootport; + portarray[INIT_PORT_AUTH] = MACH_PORT_NULL; + portarray[INIT_PORT_PROC] = MACH_PORT_NULL; + portarray[INIT_PORT_CTTYID] = MACH_PORT_NULL; + portarray[INIT_PORT_BOOTSTRAP] = port; /* use the same port */ + + *portarraypoly = MACH_MSG_TYPE_MAKE_SEND; + + *dtablepoly = MACH_MSG_TYPE_COPY_SEND; + + ports_port_deref (upt); + return 0; +} + +/* Called by S_fsys_startup for execserver bootstrap. The execserver + is able to function without a real node, hence this fraud. */ +error_t +diskfs_execboot_fsys_startup (mach_port_t port, int flags, + mach_port_t ctl, + mach_port_t *real, + mach_msg_type_name_t *realpoly) +{ + error_t err; + string_t pathbuf; + enum retry_type retry; + struct port_info *pt; + struct protid *rootpi; + mach_port_t rootport; + + if (!(pt = ports_lookup_port (diskfs_port_bucket, port, + diskfs_execboot_class))) + return EOPNOTSUPP; + + err = diskfs_create_protid (diskfs_make_peropen (diskfs_root_node, flags, 0), + 0, &rootpi); + assert_perror (err); + rootport = ports_get_right (rootpi); + mach_port_insert_right (mach_task_self (), rootport, rootport, + MACH_MSG_TYPE_MAKE_SEND); + ports_port_deref (rootpi); + + err = dir_lookup (rootport, _SERVERS_EXEC, flags|O_NOTRANS, 0, + &retry, pathbuf, real); + assert_perror (err); + assert (retry == FS_RETRY_NORMAL); + assert (pathbuf[0] == '\0'); + *realpoly = MACH_MSG_TYPE_MOVE_SEND; + + mach_port_deallocate (mach_task_self (), rootport); + + diskfs_exec_ctl = ctl; + + mutex_lock (&execstartlock); + condition_signal (&execstarted); + mutex_unlock (&execstartlock); + ports_port_deref (pt); + return 0; +} + +/* Called by init to get the privileged ports as described + in <hurd/fsys.defs>. */ +kern_return_t +diskfs_S_fsys_getpriv (mach_port_t port, + mach_port_t reply, mach_msg_type_name_t reply_type, + mach_port_t *host_priv, mach_msg_type_name_t *hp_type, + mach_port_t *dev_master, mach_msg_type_name_t *dm_type, + mach_port_t *fstask, mach_msg_type_name_t *task_type) +{ + error_t err; + struct port_info *init_bootstrap_port = + ports_lookup_port (diskfs_port_bucket, port, diskfs_initboot_class); + + if (!init_bootstrap_port) + return EOPNOTSUPP; + + err = get_privileged_ports (host_priv, dev_master); + if (!err) + { + *fstask = mach_task_self (); + *hp_type = *dm_type = MACH_MSG_TYPE_MOVE_SEND; + *task_type = MACH_MSG_TYPE_COPY_SEND; + } + + ports_port_deref (init_bootstrap_port); + + return err; +} + +/* Called by init to give us ports to the procserver and authserver as + described in <hurd/fsys.defs>. */ +kern_return_t +diskfs_S_fsys_init (mach_port_t port, + mach_port_t reply, mach_msg_type_name_t replytype, + mach_port_t procserver, + mach_port_t authhandle) +{ + struct port_infe *pt; + static int initdone = 0; + mach_port_t host, startup; + error_t err; + mach_port_t root_pt; + struct protid *rootpi; + + pt = ports_lookup_port (diskfs_port_bucket, port, diskfs_initboot_class); + if (!pt) + return EOPNOTSUPP; + ports_port_deref (pt); + if (initdone) + return EOPNOTSUPP; + initdone = 1; + + /* init is single-threaded, so we must reply to its RPC before doing + anything which might attempt to send an RPC to init. */ + fsys_init_reply (reply, replytype, 0); + + /* Allocate our references here; _hurd_init will consume a reference + for the library itself. */ + err = mach_port_mod_refs (mach_task_self (), + procserver, MACH_PORT_RIGHT_SEND, +1); + assert_perror (err); + err = mach_port_mod_refs (mach_task_self (), + authhandle, MACH_PORT_RIGHT_SEND, +1); + assert_perror (err); + + if (diskfs_auth_server_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), diskfs_auth_server_port); + diskfs_auth_server_port = authhandle; + + if (diskfs_exec_server_task != MACH_PORT_NULL) + { + process_t execprocess; + err = proc_task2proc (procserver, diskfs_exec_server_task, &execprocess); + assert_perror (err); + + /* Declare that the exec server is our child. */ + proc_child (procserver, diskfs_exec_server_task); + proc_mark_exec (execprocess); + + /* Don't start this until now so that exec is fully authenticated + with proc. */ + HURD_PORT_USE (&_diskfs_exec_portcell, + exec_init (port, authhandle, + execprocess, MACH_MSG_TYPE_COPY_SEND)); + mach_port_deallocate (mach_task_self (), execprocess); + + /* We don't need this anymore. */ + mach_port_deallocate (mach_task_self (), diskfs_exec_server_task); + diskfs_exec_server_task = MACH_PORT_NULL; + } + else + { + mach_port_t bootstrap; + process_t parent_proc; + + assert (parent_task != MACH_PORT_NULL); + + /* Tell the proc server that our parent task is our child. This + makes the process hierarchy fail to represent the real order of + who created whom, but it sets the owner and authentication ids to + root. It doesn't really matter that the parent fs task be + authenticated, but the exec server needs to be authenticated to + complete the boot handshakes with init. The exec server gets its + privilege by the parent fs doing proc_child (code above) after + we send it fsys_init (below). */ + + err = proc_child (procserver, parent_task); + assert_perror (err); + + /* Get the parent's proc server port so we can send it in the fsys_init + RPC just as init would. */ + err = proc_task2proc (procserver, parent_task, &parent_proc); + assert_perror (err); + + /* We don't need this anymore. */ + mach_port_deallocate (mach_task_self (), parent_task); + parent_task = MACH_PORT_NULL; + + proc_mark_exec (parent_proc); + + /* Give our parent (the real bootstrap filesystem) an fsys_init + RPC of its own, as init would have sent it. */ + err = task_get_bootstrap_port (mach_task_self (), &bootstrap); + assert_perror (err); + err = fsys_init (bootstrap, parent_proc, MACH_MSG_TYPE_MOVE_SEND, + authhandle); + mach_port_deallocate (mach_task_self (), bootstrap); + assert_perror (err); + } + + /* Get a port to the root directory to put in the library's + data structures. */ + err = diskfs_create_protid (diskfs_make_peropen (diskfs_root_node, + O_READ|O_EXEC, 0), + 0, &rootpi); + assert_perror (err); + root_pt = ports_get_right (rootpi); + ports_port_deref (rootpi); + + /* We need two send rights, for the crdir and cwdir slots. */ + mach_port_insert_right (mach_task_self (), root_pt, root_pt, + MACH_MSG_TYPE_MAKE_SEND); + mach_port_mod_refs (mach_task_self (), root_pt, + MACH_PORT_RIGHT_SEND, +1); + + if (_hurd_ports) + { + /* We already have a portarray, because somebody responded to + exec_startup on our initial bootstrap port, even though we are + supposedly the bootstrap program. The losing `boot' that runs on + UX does this. */ + _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver); /* Consume. */ + _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authhandle); /* Consume. */ + _hurd_port_set (&_hurd_ports[INIT_PORT_CRDIR], root_pt); /* Consume. */ + _hurd_port_set (&_hurd_ports[INIT_PORT_CWDIR], root_pt); /* Consume. */ + _hurd_proc_init (diskfs_argv, NULL, 0); + } + else + { + /* We have no portarray or intarray because there was + no exec_startup data; _hurd_init was never called. + We now have the crucial ports, so create a portarray + and call _hurd_init. */ + mach_port_t *portarray; + unsigned int i; + portarray = mmap (0, INIT_PORT_MAX * sizeof *portarray, + PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (MACH_PORT_NULL != (mach_port_t) 0) + for (i = 0; i < INIT_PORT_MAX; ++i) + portarray[i] = MACH_PORT_NULL; + portarray[INIT_PORT_PROC] = procserver; + portarray[INIT_PORT_AUTH] = authhandle; + portarray[INIT_PORT_CRDIR] = root_pt; + portarray[INIT_PORT_CWDIR] = root_pt; + _hurd_init (0, diskfs_argv, portarray, INIT_PORT_MAX, NULL, 0); + } + + err = get_privileged_ports (&host, 0); + if (err) + return err; + + proc_register_version (procserver, host, diskfs_server_name, "", + diskfs_server_version); + + err = proc_getmsgport (procserver, 1, &startup); + if (!err) + { + startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL, + diskfs_server_name, host); + mach_port_deallocate (mach_task_self (), startup); + } + + mach_port_deallocate (mach_task_self (), host); + mach_port_deallocate (mach_task_self (), procserver); + + _diskfs_init_completed (); + + return MIG_NO_REPLY; /* Already replied above. */ +} + +/* Start the execserver running (when we are a bootstrap filesystem). */ +static void +start_execserver (void) +{ + error_t err; + mach_port_t right; + extern task_t diskfs_exec_server_task; /* Set in opts-std-startup.c. */ + struct port_info *execboot_info; + + assert (diskfs_exec_server_task != MACH_PORT_NULL); + + err = ports_create_port (diskfs_execboot_class, diskfs_port_bucket, + sizeof (struct port_info), &execboot_info); + assert_perror (err); + right = ports_get_right (execboot_info); + mach_port_insert_right (mach_task_self (), right, + right, MACH_MSG_TYPE_MAKE_SEND); + ports_port_deref (execboot_info); + task_set_special_port (diskfs_exec_server_task, TASK_BOOTSTRAP_PORT, right); + mach_port_deallocate (mach_task_self (), right); + + if (index (diskfs_boot_flags, 'd')) + { + printf ("pausing for exec\n"); + getc (stdin); + } + task_resume (diskfs_exec_server_task); + + printf (" exec"); + fflush (stdout); +} |