summaryrefslogtreecommitdiff
path: root/boot/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/boot.c')
-rw-r--r--boot/boot.c282
1 files changed, 275 insertions, 7 deletions
diff --git a/boot/boot.c b/boot/boot.c
index ab9c1648..1cc94bce 100644
--- a/boot/boot.c
+++ b/boot/boot.c
@@ -1,6 +1,6 @@
/* Load a task using the single server, and then run it
as if we were the kernel.
- Copyright (C) 1993,94,95,96,97,98,99,2000,01,02,2006
+ Copyright (C) 1993,94,95,96,97,98,99,2000,01,02,2006,14,16
Free Software Foundation, Inc.
This file is part of the GNU Hurd.
@@ -26,6 +26,7 @@
#include <device/device.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
+#include <mach/task_notify.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -35,6 +36,8 @@
#include <mach/default_pager.h>
#include <argp.h>
#include <hurd/store.h>
+#include <hurd/ihash.h>
+#include <sys/reboot.h>
#include <sys/mman.h>
#include <version.h>
@@ -46,6 +49,10 @@
#include "term_S.h"
#include "bootstrap_S.h"
/* #include "tioctl_S.h" */
+#include "mach_S.h"
+#include "mach_host_S.h"
+#include "gnumach_S.h"
+#include "task_notify_S.h"
#include "boot_script.h"
@@ -61,6 +68,14 @@
#include <hurd.h>
#include <assert.h>
+/* We support two modes of operation. Traditionally, Subhurds were
+ privileged, i.e. they had the privileged kernel ports. This has a
+ few drawbacks. Privileged subhurds can manipulate all tasks on the
+ system and halt the system. Nowadays we allow an unprivileged
+ mode. */
+static int privileged;
+static int want_privileged;
+
static struct termios orig_tty_state;
static int isig;
static char *kernel_command_line;
@@ -99,9 +114,14 @@ host_exit (int status)
}
mach_port_t privileged_host_port, master_device_port;
+mach_port_t pseudo_privileged_host_port;
mach_port_t pseudo_master_device_port;
mach_port_t receive_set;
mach_port_t pseudo_console, pseudo_root, pseudo_time;
+mach_port_t pseudo_pset;
+task_t pseudo_kernel;
+mach_port_t task_notification_port;
+mach_port_t dead_task_notification_port;
auth_t authserver;
struct store *root_store;
@@ -177,7 +197,11 @@ boot_demuxer (mach_msg_header_t *inp,
if ((routine = io_server_routine (inp)) ||
(routine = device_server_routine (inp)) ||
(routine = notify_server_routine (inp)) ||
- (routine = term_server_routine (inp))
+ (routine = term_server_routine (inp)) ||
+ (routine = mach_server_routine (inp)) ||
+ (routine = mach_host_server_routine (inp)) ||
+ (routine = gnumach_server_routine (inp)) ||
+ (routine = task_notify_server_routine (inp))
/* (routine = tioctl_server_routine (inp)) */)
{
(*routine) (inp, outp);
@@ -192,6 +216,8 @@ void * msg_thread (void *);
const char *argp_program_version = STANDARD_HURD_VERSION (boot);
+#define OPT_PRIVILEGED -1
+
static struct argp_option options[] =
{
{ "boot-root", 'D', "DIR", 0,
@@ -206,6 +232,8 @@ static struct argp_option options[] =
"Do not disable terminal signals, so you can suspend and interrupt boot."},
{ "device", 'f', "device_name=device_file", 0,
"Specify a device file used by subhurd and its virtual name."},
+ { "privileged", OPT_PRIVILEGED, NULL, 0,
+ "Allow the subhurd to access privileged kernel ports"},
{ 0 }
};
static char args_doc[] = "BOOT-SCRIPT";
@@ -277,6 +305,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
add_dev_map (arg, dev_file+1);
break;
+ case OPT_PRIVILEGED:
+ want_privileged = 1;
+ break;
+
case ARGP_KEY_ARG:
if (state->arg_num == 0)
bootscript = arg;
@@ -293,6 +325,55 @@ parse_opt (int key, char *arg, struct argp_state *state)
return 0;
}
+static error_t
+allocate_pseudo_ports (void)
+{
+ mach_port_t old;
+
+ /* Allocate a port that we hand out as the privileged host port. */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pseudo_privileged_host_port);
+ mach_port_insert_right (mach_task_self (),
+ pseudo_privileged_host_port,
+ pseudo_privileged_host_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ mach_port_move_member (mach_task_self (), pseudo_privileged_host_port,
+ receive_set);
+ mach_port_request_notification (mach_task_self (),
+ pseudo_privileged_host_port,
+ MACH_NOTIFY_NO_SENDERS, 1,
+ pseudo_privileged_host_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+ assert (old == MACH_PORT_NULL);
+
+ /* Allocate a port that we hand out as the privileged processor set
+ port. */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pseudo_pset);
+ mach_port_move_member (mach_task_self (), pseudo_pset,
+ receive_set);
+ /* Make one send right that we copy when handing it out. */
+ mach_port_insert_right (mach_task_self (),
+ pseudo_pset,
+ pseudo_pset,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ /* We will receive new task notifications on this port. */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &task_notification_port);
+ mach_port_move_member (mach_task_self (), task_notification_port,
+ receive_set);
+
+ /* And information about dying tasks here. */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &dead_task_notification_port);
+ mach_port_move_member (mach_task_self (), dead_task_notification_port,
+ receive_set);
+
+ return 0;
+}
+
+
int
main (int argc, char **argv, char **envp)
{
@@ -315,16 +396,26 @@ main (int argc, char **argv, char **envp)
if (err)
error (4, err, "%s", root_store_name);
- get_privileged_ports (&privileged_host_port, &master_device_port);
+ if (want_privileged)
+ {
+ get_privileged_ports (&privileged_host_port, &master_device_port);
+ privileged = MACH_PORT_VALID (master_device_port);
+
+ if (! privileged)
+ error (1, 0, "Must be run as root for privileged subhurds");
+ }
- strcat (bootstrap_args, "f");
+ if (privileged)
+ strcat (bootstrap_args, "f");
mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET,
&receive_set);
if (root_store->class == &store_device_class && root_store->name
&& (root_store->flags & STORE_ENFORCED)
- && root_store->num_runs == 1 && root_store->runs[0].start == 0)
+ && root_store->num_runs == 1
+ && root_store->runs[0].start == 0
+ && privileged)
/* Let known device nodes pass through directly. */
bootdevice = root_store->name;
else
@@ -369,13 +460,33 @@ main (int argc, char **argv, char **envp)
if (foo != MACH_PORT_NULL)
mach_port_deallocate (mach_task_self (), foo);
+ if (! privileged)
+ {
+ err = allocate_pseudo_ports ();
+ if (err)
+ error (1, err, "Allocating pseudo ports");
+
+ /* Create a new task namespace for us. */
+ err = proc_make_task_namespace (getproc (), task_notification_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (1, err, "proc_make_task_namespace");
+
+ /* Create an empty task that the subhurds can freely frobnicate. */
+ err = task_create (mach_task_self (), 0, &pseudo_kernel);
+ if (err)
+ error (1, err, "task_create");
+ }
+
if (kernel_command_line == 0)
asprintf (&kernel_command_line, "%s %s root=%s",
argv[0], bootstrap_args, bootdevice);
/* Initialize boot script variables. */
if (boot_script_set_variable ("host-port", VAL_PORT,
- (int) privileged_host_port)
+ privileged
+ ? (int) privileged_host_port
+ : (int) pseudo_privileged_host_port)
|| boot_script_set_variable ("device-port", VAL_PORT,
(integer_t) pseudo_master_device_port)
|| boot_script_set_variable ("kernel-command-line", VAL_STR,
@@ -1115,6 +1226,8 @@ do_mach_notify_send_once (mach_port_t notify)
return EOPNOTSUPP;
}
+static void task_died (mach_port_t name);
+
kern_return_t
do_mach_notify_dead_name (mach_port_t notify,
mach_port_t name)
@@ -1123,7 +1236,11 @@ do_mach_notify_dead_name (mach_port_t notify,
if (name == child_task && notify == bootport)
host_exit (0);
#endif
- return EOPNOTSUPP;
+ if (notify != dead_task_notification_port)
+ return EOPNOTSUPP;
+ task_died (name);
+ mach_port_deallocate (mach_task_self (), name);
+ return 0;
}
@@ -1389,6 +1506,8 @@ S_io_reauthenticate (mach_port_t object,
size_t gulen = 0, aulen = 0, gglen = 0, aglen = 0;
error_t err;
+ /* XXX: This cannot possibly work, authserver is 0. */
+
err = mach_port_insert_right (mach_task_self (), object, object,
MACH_MSG_TYPE_MAKE_SEND);
assert_perror (err);
@@ -1681,3 +1800,152 @@ kern_return_t S_term_on_pty
io_t *ptymaster
)
{ return EOPNOTSUPP; }
+
+/* Mach host emulation. */
+
+kern_return_t
+S_vm_set_default_memory_manager (mach_port_t host_priv,
+ mach_port_t *default_manager)
+{
+ if (host_priv != pseudo_privileged_host_port)
+ return KERN_INVALID_HOST;
+
+ if (*default_manager != MACH_PORT_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ *default_manager = MACH_PORT_NULL;
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+S_host_reboot (mach_port_t host_priv,
+ int flags)
+{
+ fprintf (stderr, "Would %s the system. Bye.\n",
+ flags & RB_HALT? "halt": "reboot");
+ host_exit (0);
+}
+
+
+kern_return_t
+S_host_processor_set_priv (mach_port_t host_priv,
+ mach_port_t set_name,
+ mach_port_t *set)
+{
+ if (host_priv != pseudo_privileged_host_port)
+ return KERN_INVALID_HOST;
+
+ *set = pseudo_pset;
+ return KERN_SUCCESS;
+}
+
+mach_port_t new_task_notification;
+
+kern_return_t
+S_register_new_task_notification (mach_port_t host_priv,
+ mach_port_t notification)
+{
+ if (host_priv != pseudo_privileged_host_port)
+ return KERN_INVALID_HOST;
+
+ if (! MACH_PORT_VALID (notification))
+ return KERN_INVALID_ARGUMENT;
+
+ if (MACH_PORT_VALID (new_task_notification))
+ return KERN_NO_ACCESS;
+
+ new_task_notification = notification;
+ return KERN_SUCCESS;
+}
+
+
+/* Managing tasks. */
+
+static void
+task_ihash_cleanup (hurd_ihash_value_t value, void *cookie)
+{
+ (void) cookie;
+ mach_port_deallocate (mach_task_self (), (mach_port_t) value);
+}
+
+static struct hurd_ihash task_ihash =
+ HURD_IHASH_INITIALIZER_GKI (HURD_IHASH_NO_LOCP, task_ihash_cleanup, NULL,
+ NULL, NULL);
+
+static void
+task_died (mach_port_t name)
+{
+ hurd_ihash_remove (&task_ihash, (hurd_ihash_key_t) name);
+}
+
+/* Handle new task notifications from proc. */
+error_t
+S_mach_notify_new_task (mach_port_t notify,
+ mach_port_t task,
+ mach_port_t parent)
+{
+ error_t err;
+ mach_port_t previous;
+
+ if (notify != task_notification_port)
+ return EOPNOTSUPP;
+
+ err = mach_port_request_notification (mach_task_self (), task,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ dead_task_notification_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &previous);
+ if (err)
+ goto fail;
+ assert (! MACH_PORT_VALID (previous));
+
+ err = hurd_ihash_add (&task_ihash,
+ (hurd_ihash_key_t) task, (hurd_ihash_value_t) task);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ goto fail;
+ }
+
+ if (MACH_PORT_VALID (new_task_notification))
+ /* Relay the notification. */
+ mach_notify_new_task (new_task_notification, task, parent);
+
+ mach_port_deallocate (mach_task_self (), parent);
+ return 0;
+
+ fail:
+ task_terminate (task);
+ return err;
+}
+
+kern_return_t
+S_processor_set_tasks(mach_port_t processor_set,
+ task_array_t *task_list,
+ mach_msg_type_number_t *task_listCnt)
+{
+ error_t err;
+ size_t i;
+
+ err = vm_allocate (mach_task_self (), (vm_address_t *) task_list,
+ task_ihash.nr_items * sizeof **task_list, 1);
+ if (err)
+ return err;
+
+ /* The first task has to be the kernel. */
+ (*task_list)[0] = pseudo_kernel;
+
+ i = 1;
+ HURD_IHASH_ITERATE (&task_ihash, value)
+ {
+ task_t task = (task_t) value;
+ if (task == pseudo_kernel)
+ continue;
+
+ (*task_list)[i] = task;
+ i += 1;
+ }
+
+ *task_listCnt = task_ihash.nr_items;
+ return 0;
+}