summaryrefslogtreecommitdiff
path: root/debian/patches/bootshell0007-XXX-bootshell.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/bootshell0007-XXX-bootshell.patch')
-rw-r--r--debian/patches/bootshell0007-XXX-bootshell.patch4500
1 files changed, 4500 insertions, 0 deletions
diff --git a/debian/patches/bootshell0007-XXX-bootshell.patch b/debian/patches/bootshell0007-XXX-bootshell.patch
new file mode 100644
index 00000000..b3e78a2a
--- /dev/null
+++ b/debian/patches/bootshell0007-XXX-bootshell.patch
@@ -0,0 +1,4500 @@
+From aba884a4ee96aa2e7a0383490bfd539210b040c5 Mon Sep 17 00:00:00 2001
+From: Justus Winter <4winter@informatik.uni-hamburg.de>
+Date: Tue, 23 Dec 2014 15:03:44 +0100
+Subject: [PATCH hurd 07/11] XXX bootshell
+
+XXX hack in toplevel Makefile.
+---
+ Makefile | 1 +
+ bootshell/Makefile | 73 +++
+ bootshell/boot.scm | 214 ++++++++
+ bootshell/bootshell.h | 49 ++
+ bootshell/bootstrap.scm | 306 ++++++++++++
+ bootshell/elf-exec.c | 211 ++++++++
+ bootshell/exceptions.c | 92 ++++
+ bootshell/exec-startup.c | 182 +++++++
+ bootshell/ffi.c | 1215 +++++++++++++++++++++++++++++++++++++++++++++
+ bootshell/ffi.h | 170 +++++++
+ bootshell/frob-task.c | 131 +++++
+ bootshell/fs.c | 111 +++++
+ bootshell/fsys.c | 164 ++++++
+ bootshell/fsys.h | 28 ++
+ bootshell/hurd.scm | 122 +++++
+ bootshell/mach.scm | 93 ++++
+ bootshell/main.c | 267 ++++++++++
+ bootshell/mig-decls.h | 22 +
+ bootshell/mig-mutate.h | 27 +
+ bootshell/runsystem.scm | 78 +++
+ bootshell/scheme-config.h | 31 ++
+ bootshell/startup.c | 508 +++++++++++++++++++
+ bootshell/startup.h | 36 ++
+ bootshell/utils.c | 118 +++++
+ config.make.in | 4 +
+ configure.ac | 15 +
+ 26 files changed, 4268 insertions(+)
+ create mode 100644 bootshell/Makefile
+ create mode 100644 bootshell/boot.scm
+ create mode 100644 bootshell/bootshell.h
+ create mode 100644 bootshell/bootstrap.scm
+ create mode 100644 bootshell/elf-exec.c
+ create mode 100644 bootshell/exceptions.c
+ create mode 100644 bootshell/exec-startup.c
+ create mode 100644 bootshell/ffi.c
+ create mode 100644 bootshell/ffi.h
+ create mode 100644 bootshell/frob-task.c
+ create mode 100644 bootshell/fs.c
+ create mode 100644 bootshell/fsys.c
+ create mode 100644 bootshell/fsys.h
+ create mode 100644 bootshell/hurd.scm
+ create mode 100644 bootshell/mach.scm
+ create mode 100644 bootshell/main.c
+ create mode 100644 bootshell/mig-decls.h
+ create mode 100644 bootshell/mig-mutate.h
+ create mode 100644 bootshell/runsystem.scm
+ create mode 100644 bootshell/scheme-config.h
+ create mode 100644 bootshell/startup.c
+ create mode 100644 bootshell/startup.h
+ create mode 100644 bootshell/utils.c
+
+diff --git a/Makefile b/Makefile
+index 6544cd2..00d64ff 100644
+--- a/Makefile
++++ b/Makefile
+@@ -32,6 +32,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
+
+ # Hurd programs
+ prog-subdirs = auth proc exec term \
++ bootshell \
+ ext2fs isofs tmpfs fatfs \
+ storeio pflocal pfinet defpager mach-defpager \
+ login daemons boot console \
+diff --git a/bootshell/Makefile b/bootshell/Makefile
+new file mode 100644
+index 0000000..e2b09e2
+--- /dev/null
++++ b/bootshell/Makefile
+@@ -0,0 +1,73 @@
++# Makefile for bootshell subdirectory of hurd sources
++#
++# Copyright (C) 1999, 2000, 2002, 2007, 2010, 2012 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 this program; if not, write to the Free Software
++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++dir := bootshell
++makemode:= server
++target := bootshell
++
++SCRIPTS := \
++ init.scm \
++ mach.scm \
++ hurd.scm \
++ boot.scm \
++ bootstrap.scm \
++ runsystem.scm \
++
++SRCS := \
++ scheme.c \
++ main.c \
++ exceptions.c \
++ fs.c \
++ fsys.c \
++ exec-startup.c \
++ startup.c \
++ utils.c \
++ ffi.c \
++ hurd.c \
++ elf-exec.c \
++ frob-task.c \
++
++SERVER_PROTOCOLS := exc exec_startup fs fsys startup
++USER_PROTOCOLS := startup startup_reply
++
++MIGSTUBS:= $(foreach p,$(SERVER_PROTOCOLS),$(p)Server.o) \
++ $(foreach p,$(USER_PROTOCOLS),$(p)User.o)
++OBJS := $(SRCS:.c=.o) $(SCRIPTS:.scm=.o) $(MIGSTUBS)
++
++HURDLIBS:= shouldbeinlibc
++# XXX why doesn't $(libreadline_LIBS) work ???
++OTHERLIBS:= $(libreadline_LIBS) -lreadline -lhistory -lncurses -ltinfo -lpthread
++CFLAGS += -imacros scheme-config.h
++LDFLAGS += -static
++MIGSFLAGS := -imacros mig-mutate.h
++
++%.o: %.scm
++ cat <$< >.$@
++ $(LD) -r --format=binary .$@ -o $@
++ rm .$@
++
++NOWARN := conversion sign-conversion switch unused-function
++CFLAGS := $(filter-out $(foreach flag,$NOWARN,-W$(flag)),$(CFLAGS))
++
++# XXX
++CFLAGS += -Wno-sign-conversion
++
++include ../Makeconf
+diff --git a/bootshell/boot.scm b/bootshell/boot.scm
+new file mode 100644
+index 0000000..75cc75f
+--- /dev/null
++++ b/bootshell/boot.scm
+@@ -0,0 +1,214 @@
++;; Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
++;;
++;; Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++;; Missing library functions.
++(define (filter pred lst)
++ (cond ((null? lst) '())
++ ((pred (car lst))
++ (cons (car lst) (filter pred (cdr lst))))
++ (else (filter pred (cdr lst)))))
++
++(define (any p l)
++ (cond ((null? l) #f)
++ ((p (car l)) #t)
++ (else (any p (cdr l)))))
++
++;; Is s1 a prefix of s2 ?
++(define (string-prefix? s1 s2)
++ (and (>= (string-length s2) (string-length s1))
++ (string=? s1 (substring s2 0 (string-length s1)))))
++
++;; Given a list of prefixes, does s start with any of them ?
++(define (string-prefix-any? lp s)
++ (any (lambda (p) (string-prefix? p s)) lp))
++
++;; The `catch' from init.scm doesn't give the thrown value to the
++;; handler. As a crappy workaround, we set! `last-exception' to the
++;; last the exception.
++(define last-exception '())
++(define (throw . x)
++ (set! last-exception x)
++ (if (more-handlers?)
++ (apply (pop-handler))
++ (apply error x)))
++(define *error-hook* throw)
++
++;; Foreign function wrapper. Expects F to return a list with the
++;; first element being the `error_t' value returned by the foreign
++;; function. The error is thrown, or the cdr of the result is
++;; returned.
++(define (ffi-apply name f args)
++ (let ((result (apply f args)))
++ (cond
++ ((not (= (car result) 0))
++ (let ((args' (open-output-string)))
++ (write (cons (string->symbol name) args) args')
++ (throw (string-append
++ (get-output-string args') ": " (strerror (car result))))))
++ ((and (= (car result) 0) (pair? (cdr result))) (cadr result))
++ ((= (car result) 0) '())
++ (else
++ (throw "Weird.")))))
++
++;; Convenience functions.
++(define (echo . msg)
++ (map display msg)
++ (newline))
++
++(define (trace-show x)
++ (write x)
++ x)
++
++;; Semi-crappy repl using `prompt' function.
++(define (shell p)
++ (call/cc
++ (lambda (exit)
++ (let loop ((prefix ""))
++ (let ((line (prompt (p prefix))))
++ (if (and (not (eof-object? line)) (eqv? (string-length line) 0))
++ (exit (loop prefix)))
++ (if (not (eof-object? line))
++ (let* ((next (string-append prefix line))
++ (c (catch (begin (echo "Parse error: " last-exception)
++ (loop prefix))
++ (read (open-input-string next)))))
++ (if (not (eof-object? c))
++ (begin
++ (catch (echo "Error: " last-exception)
++ (echo " ===> " (eval c)))
++ (exit (loop ""))))
++ (exit (loop next)))))))))
++
++(define (prompt-append-prefix prompt prefix)
++ (string-append prompt (if (> (string-length prefix) 0)
++ (string-append prefix "...")
++ "> ")))
++
++;; Default repl run by the bootshell.
++(define (interactive-repl)
++ (shell (lambda (p) (prompt-append-prefix "(bootshell) " p))))
++
++;; Default repl run by `panic'.
++(define (emergency-shell)
++ (shell (lambda (p) (prompt-append-prefix "(emergency-shell) " p))))
++
++;; Display a message and run the emergency shell.
++(define (panic . msg)
++ (display "\n\npanic: ")
++ (map display msg)
++ (newline)
++ (emergency-shell))
++
++;; XXX
++(define (path->string x)
++ (if (string? x) x (symbol->string x)))
++
++;; Hurd server bootstrap.
++
++;; Wait for the predicate CONDITION to return #t, or throw 'timeout
++;; after T microseconds.
++(define (wait-for condition t)
++ (if (<= t 0)
++ (throw 'timeout)
++ (if (not (condition))
++ (begin (usleep 10000)
++ (wait-for condition (- t 10000))))))
++
++;; Read a word from port P.
++(define (read-word p)
++ (list->string
++ (let f ()
++ (let ((c (peek-char p)))
++ (cond
++ ((eof-object? c) '())
++ ((char-alphabetic? c)
++ (read-char p)
++ (cons c (f)))
++ (else '()))))))
++
++;; Read everything from port P.
++(define (read-all p)
++ (list->string
++ (let f ()
++ (let ((c (peek-char p)))
++ (cond
++ ((eof-object? c) '())
++ (else (read-char p)
++ (cons c (f))))))))
++
++;; Shell-like functions.
++
++(define cd chdir)
++(define (pwd) (echo (getcwd)))
++(define (cat path)
++ (display (call-with-input-file path read-all)))
++(define (hostname)
++ ((lambda (x) (if (string? x) x "unnamed"))
++ (call-with-input-file "/etc/hostname" read-word)))
++
++(define (print-banner)
++ (echo "
++Welcome to bootshell, a scheme shell. Type `(help)' for help.
++"))
++
++;; Online documentation.
++
++(define (help)
++ (echo "Welcome to the Hurd boot shell. XXX this is not up to date :(
++
++Functions
++ General shell-like functions
++ cat cd echo halt help hostname kdb mach-print panic prompt pwd
++ reboot shell sleep {reboot,halt}-{mach,hurd}
++" "
++ Mach related
++ mach-port-valid? {copy,make}-send-right task-{create,resume,terminate}
++ task-{g,s}et-{special,kernel,exception,bootstrap}-port host-reboot
++" "
++ Hurd related
++ file_name_lookup chdir getcwd startup-reboot
++ XXX write them
++ {s,g}etauth {s,g}etproc file_name_lookup_under file_name_path_lookup
++" "
++Environment:
++ mach-task-self exception-port bootstrap-port host-priv device-master
++ rootfs-task hello-task rootfs-control"))
++
++;; Inserts RIGHT into TASK and returns a command line argument OPTION
++;; with the value set to the name of RIGHT in TASK.
++(define (make-arg option task right)
++ (string-append "--" option "="
++ (number->string (task-insert-send-right task right))))
++
++;; We store our messages so that we can replay them if we start the
++;; Hurd console which erases the screen.
++(define messages '())
++(define (log . args)
++ (set! messages (append messages args))
++ (for-each display args))
++(define (replay-log)
++ (for-each display messages))
++
++(define timeout 1000) ; 1 second
++
++(define (pause)
++ (if (= 1 boot-pause) (prompt "Press enter to continue...")))
++
++;; We're ready.
++(echo version ".")
+diff --git a/bootshell/bootshell.h b/bootshell/bootshell.h
+new file mode 100644
+index 0000000..fd91ce9
+--- /dev/null
++++ b/bootshell/bootshell.h
+@@ -0,0 +1,49 @@
++/* Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _HURD_BOOTSHELL_H
++#define _HURD_BOOTSHELL_H
++
++#include <mach.h>
++
++extern char **global_argv;
++extern const char *argp_program_version;
++
++extern mach_port_t portarray_template[];
++
++/* We catch exceptions using this port. */
++extern mach_port_t exception_port;
++
++extern mach_port_t console;
++extern mach_port_t rootnode;
++
++error_t init_exception_handling (void);
++error_t init_fs_server (void);
++
++mach_msg_return_t
++mach_msg_server_timeout_once (boolean_t (*demux) (mach_msg_header_t *request,
++ mach_msg_header_t *reply),
++ mach_msg_size_t max_size,
++ mach_port_t rcv_name,
++ mach_msg_option_t option,
++ mach_msg_timeout_t timeout);
++
++#define TRACE error (0, 0, "%s:%d", __FUNCTION__, __LINE__);
++
++#endif /* _HURD_BOOTSHELL_H */
+diff --git a/bootshell/bootstrap.scm b/bootshell/bootstrap.scm
+new file mode 100644
+index 0000000..a521e01
+--- /dev/null
++++ b/bootshell/bootstrap.scm
+@@ -0,0 +1,306 @@
++;; The Hurd server bootstrap.
++;;
++;; Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++;;
++;; The Hurd server bootstrap.
++;;
++
++(define (run-stage stage)
++ (stage))
++
++(define (bootstrap stages)
++ (catch (panic "Hurd bootstrap failed: " (car last-exception) "\n")
++ (log "Hurd server bootstrap: ")
++ (for-each run-stage stages)
++ (log "done.\n"))
++
++ (shell
++ (lambda (prefix)
++ (prompt-append-prefix
++ (string-append "runsystem@" (hostname) " " (getcwd) " ") prefix))))
++
++;;
++;; The stages of the Hurd server bootstrap.
++;;
++
++(define rootfs-control MACH_PORT_NULL)
++(define console-device "/dev/console")
++
++(define (first-stage rootfs-device)
++ (set! exec-server-task (task-create mach-task-self 0))
++ (let
++ ((rootfs-args
++ `("rootfs"
++ ,(make-arg "host-priv-port" rootfs-server-task host-priv)
++ ,(make-arg "device-master-port" rootfs-server-task device-master)
++ ,(make-arg "exec-server-task" rootfs-server-task exec-server-task)
++ "-T" "typed" ,rootfs-device)))
++ (log "rootfs ")
++ (set! rootfs-control (bind-root (resume-translator rootfs-server-task
++ rootfs-args)))
++ (log "/servers/exec ")
++ (task-set-name exec-server-task "/hurd/exec")
++ (task-suspend exec-server-task)
++ (elf-exec exec-server-task
++ `("/lib/ld.so.1" "/hurd/exec"
++ ,(make-arg "device-master-port" exec-server-task device-master)))
++ (bind "/servers/exec" (resume-translator exec-server-task '()))))
++
++(define (early-startup)
++ (let ((startup-control (mach-port-allocate mach-task-self
++ MACH_PORT_RIGHT_RECEIVE)))
++ (start-handling-early-startup startup-control)
++ (set-active-translator "/servers/startup" 0 0
++ (make-send-right startup-control))))
++
++(define (second-stage)
++ (letport
++ ((proc-task (task-create mach-task-self 0))
++ (auth-task (task-create mach-task-self 0)))
++
++ ;; Starting proc and auth is tricky, we need to do it simultaneously.
++ (let ((pc (bootstrap-proc (start-translator proc-task '("/hurd/proc"))))
++ (ac (bootstrap-auth (start-translator auth-task '("/hurd/auth"))))
++ ;; Projections for the cookies returned by bootstrap-*.
++ (:reply car) (:replyPoly cadr) (:server caddr))
++ (log "proc ")
++ (startup-procinit-reply (:reply pc) (:replyPoly pc) ESUCCESS
++ mach-task-self (:server ac)
++ host-priv device-master)
++ (bind-proc (:server pc))
++
++ ;; Declare that these tasks are our children, and fix them up.
++ (map fixup-task (list rootfs-server-task exec-server-task
++ proc-task auth-task))
++
++ (log "auth ")
++ (startup-authinit-reply (:reply ac) (:replyPoly ac) ESUCCESS
++ (proc->task->proc (:server pc) auth-task))
++ (bind-auth (:server ac))
++
++ ;; Give the rootfs its proc and auth port.
++ (fsys-init rootfs-control
++ (proc->task->proc (:server pc) rootfs-server-task)
++ (:server ac))
++
++ ;; Supply the proc server with a standard template.
++ (proc->auth->set-std-execdata! (:server pc) (:server ac))
++
++ ;; Neither the kernel nor our bootscript task have command line
++ ;; arguments. Fix that.
++ (frob-task (proc->pid->task (:server pc) 3)
++ '(gnumach huhu lala XXX))
++ (if (mach-port-valid? bootscript-task)
++ (frob-task bootscript-task '(/hurd/runsystem.scm)))
++ (frob-task mach-task-self '(/hurd/bootshell))
++
++ (mach-port-deallocate mach-task-self (:server pc))
++ (mach-port-deallocate mach-task-self (:server ac)))))
++
++(define (start-hurd-console)
++ (log "hurd-console ")
++ (throw 'notyet) ;; XXX
++ (run '(/bin/console
++ --driver-path=/usr/lib/hurd/console ;; XXX
++ --driver=current_vcs
++ --driver=vga
++ --driver=pc_kbd --keymap us
++ --driver=pc_mouse --protocol=ps/2
++ /dev/vcs))
++
++ ;; XXX
++ ;(symlink 'tty1 '/dev/console)
++ (set! console-device "/dev/tty1"))
++
++(define (start-mach-console)
++ (start-active-translator '/dev/console
++ '(/hurd/term /dev/console device console)))
++
++(define (start-terminal)
++ (catch (begin (log "failed: ")
++ (log last-exception)
++ (start-mach-console))
++ (start-hurd-console))
++
++ (letport ((term (file-name-lookup console-device O_RDWR 0)))
++ (bind-term term))
++
++ (if (equal? console-device "/dev/tty1")
++ ;; If we got the Hurd console running, it erased the screen.
++ (replay-log))
++
++ ;; If we made it this far, we can use libreadline!
++ (enable-readline))
++
++(define (pflocal)
++ (start-active-translator '/servers/socket/1 '(/hurd/pflocal)))
++
++(define (mach-defpager)
++ (log "mach-defpager ")
++ (run '(/hurd/mach-defpager))
++
++ ;; Wait for it to start.
++ (wait-for have-default-memory-manager? 1000000))
++
++(define (rootfs-update)
++ (fsys-set-options rootfs-control '("--update") 0))
++
++(define (startup-standalone)
++ ;; The standalone startup server watches essential servers, and
++ ;; handles the system shutdown.
++ (start-active-translator '/servers/startup '(/hurd/startup-standalone))
++
++ ;; Now that we have startup, register all servers to it.
++ (letport
++ ((startup (file-name-lookup "/servers/startup" 0 0)))
++ (let ((:port car) (:name cdr)) ;; Projections.
++ ;; We are essential.
++ (startup-essential-task startup mach-task-self MACH_PORT_NULL
++ "bootshell" host-priv)
++ (map (lambda (c)
++ (startup-essential-task startup (:port c) MACH_PORT_NULL
++ (:name c) host-priv))
++ (get-essential-tasks))
++ (map (lambda (c)
++ (startup-request-notification startup (:port c) (:name c)))
++ (get-registered-tasks)))))
++
++(define (boot! init)
++ (run-init `(/sbin/console-run --console ,console-device -- ,init -a)))
++
++;;
++;; Utility functions.
++;;
++
++;; Returns a function that can be passed to `bootstrap-translator' to
++;; start a translator that is loaded from a disk.
++(define (start-translator task argv)
++ (lambda (bootstrap)
++ (letport ((proc (getproc)))
++ (pause)
++ (_exec (file-name-lookup (car argv) O_EXEC 0) task argv bootstrap)
++ (if (mach-port-valid? proc)
++ (let ((child-proc (proc->task->proc proc task)))
++ (proc->mark-exec! child-proc)
++ (proc->mark-important! child-proc)
++ (proc->task->child! proc task))))))
++
++;; Bootstraps a translator using the fsys protocol.
++(define (bootstrap-translator prepare-task realnode)
++ (let* ((bootstrap
++ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
++ (task
++ (prepare-task (make-send-right bootstrap))))
++ (handle-fsys-startup bootstrap realnode MACH_MSG_TYPE_COPY_SEND timeout)))
++
++;; Bootstraps the proc server using the startup protocol.
++(define (bootstrap-proc prepare-task)
++ (let* ((bootstrap
++ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
++ (task
++ (prepare-task (make-send-right bootstrap))))
++ (handle-startup-procinit bootstrap timeout)))
++
++;; Bootstraps the auth server using the startup protocol.
++(define (bootstrap-auth prepare-task)
++ (let* ((bootstrap
++ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
++ (task
++ (prepare-task (make-send-right bootstrap))))
++ (handle-startup-authinit bootstrap timeout)))
++
++;; Bootstraps a translator using the fsys protocol and installs it as
++;; root filesystem.
++(define (bind-root prepare-task)
++ (let ((control
++ (bootstrap-translator prepare-task (make-send-right rootnode))))
++ (_bind-root control rootnode)
++ control))
++
++;; Bootstraps a translator using the fsys protocol and installs it as
++;; active translator for the node PATH.
++(define (bind path prepare-task)
++ (letport ((realnode (file-name-lookup path O_NOTRANS #o666)) ;; XXX mode 0 should be fine
++ (control (bootstrap-translator prepare-task realnode)))
++ (set-active-translator path 0 0 control)
++ (copy-send-right control)))
++
++;; We start servers when the proc server is not yet around. Once the
++;; proc server is available, we use this function to update its state
++;; related to TASK.
++(define (fixup-task task)
++ (letport ((myproc (getproc))
++ (p (proc->task->proc myproc task)))
++ (proc->task->child! myproc task)
++ (proc->mark-important! p)
++ (proc->mark-exec! p)))
++
++(define (start-active-translator path args)
++ (log (path->string path) " ")
++ (bind path (start-translator (task-create mach-task-self 0) args)))
++
++;; Start the userspace init daemon.
++(define (run-init argv)
++ (letport ((proc (getproc))
++ (task (task-create mach-task-self 0))
++ (child-proc (proc->task->proc proc task)))
++ (proc->task->set-init-task! proc task)
++ (task-set-name task (car argv))
++
++ ;; XXX this is roughly what console-run does
++ ;;(tcsetpgrp 0 (proc->task->pid proc task))
++ ;;(proc->setsid! child-proc)
++ ;;(proc->make-login-coll! child-proc)
++
++ (proc->mark-exec! child-proc)
++ (proc->mark-important! child-proc)
++ (proc->task->child! child-proc mach-task-self)
++ (_exec (file-name-lookup (car argv) O_EXEC 0)
++ task argv MACH_PORT_NULL)
++ (copy-send-right task)))
++
++;;
++;; Legacy support.
++;;
++
++;; Returns a function that can be passed to `bootstrap-translator' to
++;; resume a translator loaded by the traditional bootscript mechanism.
++(define (resume-translator task args)
++ (lambda (bootstrap)
++ (task-set-exception-port task (make-send-right exception-port))
++ (task-set-bootstrap-port task bootstrap)
++ (pause)
++ (task-resume task)
++ (handle-exec-startup bootstrap args timeout)
++ task))
++
++(define (traditional-first-stage)
++ (log "rootfs ")
++ (set! rootfs-control (bind-root (resume-translator rootfs-server-task '())))
++ (log "/servers/exec ")
++ (bind "/servers/exec" (resume-translator exec-server-task '())))
++
++;; XXX fix bootstrap
++(define (traditional-boot)
++ (bootstrap (list traditional-first-stage
++ early-startup
++ second-stage
++ start-terminal
++ startup-standalone
++ (lambda () (boot! "/sbin/init")))))
+diff --git a/bootshell/elf-exec.c b/bootshell/elf-exec.c
+new file mode 100644
+index 0000000..a61addb
+--- /dev/null
++++ b/bootshell/elf-exec.c
+@@ -0,0 +1,211 @@
++/* Load an ELF image into a task without using the exec server.
++
++ Copyright (C) 1993-2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <argz.h>
++#include <assert.h>
++#include <elf.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <mach.h>
++#include <mach/machine/vm_param.h> /* For VM_XXX_ADDRESS */
++#include <stdlib.h>
++#include <string.h>
++#include <sys/ioctl.h>
++#include <sys/mman.h>
++#include <unistd.h>
++
++#include "ffi.h"
++
++error_t
++load_image (task_t t,
++ char *file,
++ vm_address_t *addr)
++{
++ error_t err;
++ int fd;
++ ssize_t count;
++ Elf32_Ehdr hdr;
++
++ fd = open (file, O_RDONLY, 0);
++ if (fd == -1)
++ return errno;
++
++ count = read (fd, &hdr, sizeof hdr);
++ if (count != sizeof hdr)
++ return errno;
++
++ if (*(Elf32_Word *) hdr.e_ident != *(Elf32_Word *) "\177ELF")
++ return EINVAL; // XXX
++
++ Elf32_Phdr phdrs[hdr.e_phnum], *ph;
++ lseek (fd, hdr.e_phoff, SEEK_SET);
++ read (fd, phdrs, sizeof phdrs);
++ for (ph = phdrs; ph < &phdrs[sizeof phdrs/sizeof phdrs[0]]; ++ph)
++ if (ph->p_type == PT_LOAD)
++ {
++ vm_address_t buf;
++ vm_size_t offs = ph->p_offset & (ph->p_align - 1);
++ vm_size_t bufsz = round_page (ph->p_filesz + offs);
++
++ buf = (vm_address_t) mmap (0, bufsz,
++ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
++
++ lseek (fd, ph->p_offset, SEEK_SET);
++ read (fd, (void *)(buf + offs), ph->p_filesz);
++
++ ph->p_memsz = ((ph->p_vaddr + ph->p_memsz + ph->p_align - 1)
++ & ~(ph->p_align - 1));
++ ph->p_vaddr &= ~(ph->p_align - 1);
++ ph->p_memsz -= ph->p_vaddr;
++
++ err = vm_allocate (t, (vm_address_t*)&ph->p_vaddr, ph->p_memsz, 0);
++ if (err)
++ return err;
++
++ err = vm_write (t, ph->p_vaddr, buf, bufsz);
++ if (err)
++ return err;
++
++ munmap ((caddr_t) buf, bufsz);
++ err = vm_protect (t, ph->p_vaddr, ph->p_memsz, 0,
++ ((ph->p_flags & PF_R) ? VM_PROT_READ : 0) |
++ ((ph->p_flags & PF_W) ? VM_PROT_WRITE : 0) |
++ ((ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0));
++ if (err)
++ return err;
++ }
++
++ *addr = hdr.e_entry;
++ return 0;
++}
++
++/* XXX refactor this mess */
++error_t
++boot_script_exec_cmd (mach_port_t task, char *path, int argc,
++ char **argv, char *strings, int stringlen)
++{
++ error_t err;
++ char *args, *p;
++ int arg_len, i;
++ size_t reg_size;
++ void *arg_pos;
++ vm_offset_t stack_start, stack_end;
++ vm_address_t startpc, str_start;
++ thread_t thread;
++
++ err = load_image (task, path, &startpc);
++ if (err)
++ return err;
++
++ arg_len = stringlen + (argc + 2) * sizeof (char *) + sizeof (integer_t);
++ arg_len += 5 * sizeof (int);
++ stack_end = VM_MAX_ADDRESS;
++ stack_start = VM_MAX_ADDRESS - 16 * 1024 * 1024;
++ err = vm_allocate (task, &stack_start, stack_end - stack_start, FALSE);
++ if (err)
++ return err;
++
++ arg_pos = (void *) ((stack_end - arg_len) & ~(sizeof (natural_t) - 1));
++ args = mmap (0, stack_end - trunc_page ((vm_offset_t) arg_pos),
++ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
++ str_start = ((vm_address_t) arg_pos
++ + (argc + 2) * sizeof (char *) + sizeof (integer_t));
++ p = args + ((vm_address_t) arg_pos & (vm_page_size - 1));
++ *(int *) p = argc;
++ p = (void *) p + sizeof (int);
++ for (i = 0; i < argc; i++)
++ {
++ *(char **) p = argv[i] - strings + (char *) str_start;
++ p = (void *) p + sizeof (char *);
++ }
++ *(char **) p = 0;
++ p = (void *) p + sizeof (char *);
++ *(char **) p = 0;
++ p = (void *) p + sizeof (char *);
++ memcpy (p, strings, stringlen);
++ memset (args, 0, (vm_offset_t)arg_pos & (vm_page_size - 1));
++ err = vm_write (task, trunc_page ((vm_offset_t) arg_pos), (vm_address_t) args,
++ stack_end - trunc_page ((vm_offset_t) arg_pos));
++ if (err)
++ return err;
++ munmap ((caddr_t) args,
++ stack_end - trunc_page ((vm_offset_t) arg_pos));
++
++ err = thread_create (task, &thread);
++ if (err)
++ return err;
++
++#ifdef i386_THREAD_STATE_COUNT
++ {
++ struct i386_thread_state regs;
++ reg_size = i386_THREAD_STATE_COUNT;
++ thread_get_state (thread, i386_THREAD_STATE,
++ (thread_state_t) &regs, &reg_size);
++ regs.eip = (int) startpc;
++ regs.uesp = (int) arg_pos;
++ err = thread_set_state (thread, i386_THREAD_STATE,
++ (thread_state_t) &regs, reg_size);
++ if (err)
++ return err;
++ }
++#else
++# error needs to be ported
++#endif
++
++ err = thread_resume (thread);
++ if (err)
++ return err;
++
++ err = mach_port_deallocate (mach_task_self (), thread);
++ assert_perror (err);
++ return 0;
++}
++
++error_t
++elf_exec (mach_port_t task, char *argz, size_t argz_len)
++{
++ error_t err;
++ size_t argc;
++ char **argv;
++
++ argc = argz_count (argz, argz_len);
++ argv = malloc ((argc + 1) * sizeof *argv);
++ if (argv == NULL)
++ return ENOMEM;
++ argz_extract (argz, argz_len, argv);
++
++ err = boot_script_exec_cmd (task, argz, argc, argv, argz, argz_len);
++ free (argv);
++ return err;
++}
++
++pointer
++do_elf_exec (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("elf-exec");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, pointer, arguments, list, args);
++ SC_ARGS_DONE (sc);
++ char *argz = NULL;
++ size_t argz_len = 0;
++ ffi_list2argz (sc, &argz, &argz_len, arguments);
++ err = elf_exec (task, argz, argz_len);
++ free (argz);
++ SC_RETURN (sc);
++}
+diff --git a/bootshell/exceptions.c b/bootshell/exceptions.c
+new file mode 100644
+index 0000000..3102369
+--- /dev/null
++++ b/bootshell/exceptions.c
+@@ -0,0 +1,92 @@
++/* Mach exception handling.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <mach.h>
++#include <pthread.h>
++#include <stdio.h>
++
++#include "bootshell.h"
++
++// eek #include "exc_S.h"
++
++error_t
++catch_exception_raise (mach_port_t e,
++ thread_t thread,
++ task_t task,
++ int exception, int code, int subcode)
++{
++ if (e != exception_port)
++ return EOPNOTSUPP;
++
++ fprintf (stderr, "catch_exception_raise (%d, %d, %d, %d, %d): ",
++ thread, task, exception, code, subcode);
++
++ if (task == mach_task_self ())
++ fprintf (stderr, "terminating bootshell. bye.\n");
++ else
++ fprintf (stderr, "terminating task %d.\n", task);
++
++ task_terminate (task);
++ return 0;
++}
++
++static void *
++service_exception_requests (void *arg)
++{
++ extern boolean_t exc_server (mach_msg_header_t *, mach_msg_header_t *);
++
++ while (1)
++ mach_msg_server (exc_server, 0, exception_port);
++
++ /* Not reached. */
++ return NULL;
++}
++
++error_t
++init_exception_handling (void)
++{
++ error_t err;
++ pthread_t t;
++
++ err = mach_port_allocate (mach_task_self (),
++ MACH_PORT_RIGHT_RECEIVE,
++ &exception_port);
++ if (err)
++ return err;
++
++ /* Make a thread to service exception requests. */
++ err = pthread_create (&t, NULL, service_exception_requests, NULL);
++ if (err)
++ return err;
++ pthread_detach (t);
++
++ err = mach_port_insert_right (mach_task_self (),
++ exception_port,
++ exception_port,
++ MACH_MSG_TYPE_MAKE_SEND);
++ if (err)
++ return err;
++
++ err = task_set_exception_port (mach_task_self (), exception_port);
++ if (err)
++ return err;
++
++ return err;
++}
++
+diff --git a/bootshell/exec-startup.c b/bootshell/exec-startup.c
+new file mode 100644
+index 0000000..026687f
+--- /dev/null
++++ b/bootshell/exec-startup.c
+@@ -0,0 +1,182 @@
++/* Handles the exec_startup protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <hurd.h>
++#include <hurd/paths.h>
++#include <mach.h>
++#include <mach/message.h>
++#include <mach/mig_support.h>
++#include <stdio.h>
++#include <sys/mman.h>
++
++// eek #include "fsys_S.h"
++
++#include "bootshell.h"
++#include "ffi.h"
++
++/* XXX would be nice not to use a global variable, maybe with
++ payloads. */
++static struct
++{
++ /* Filled by caller. */
++ mach_port_t bootstrap_port;
++ char *argz;
++ size_t argz_len;
++
++ /* Filled by the server function. */
++} exec_startup_get_info_args;
++
++/* 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
++S_exec_startup_get_info (mach_port_t bootstrap_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 **argz,
++ mach_msg_type_number_t *argz_len,
++ char **envz,
++ mach_msg_type_number_t *envz_len,
++ 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;
++
++ if (bootstrap_port != exec_startup_get_info_args.bootstrap_port)
++ return EOPNOTSUPP;
++
++ *user_entry = 0;
++ *phdr_data = *base_addr = 0;
++ *phdr_size = *stack_size = 0;
++
++ *flags = 0;
++
++ /* Arguments. */
++ *argz = NULL;
++ *argz_len = exec_startup_get_info_args.argz_len;
++ if (*argz_len == 0)
++ /* We have no args for it. Tell it to look on its stack
++ for the args placed there by the boot loader. */
++ *flags |= EXEC_STACK_ARGS;
++ else
++ {
++ err = vm_allocate (mach_task_self (),
++ (vm_address_t *)argz, *argz_len, TRUE);
++ if (err)
++ return err;
++ memcpy (*argz, exec_startup_get_info_args.argz, *argz_len);
++ }
++
++ /* Environment. */
++ *envz = NULL;
++ *envz_len = 0;
++
++ /* File descriptors. */
++ if (*dtablelen < 3)
++ *dtableP = mmap (0, 3 * sizeof (mach_port_t), PROT_READ|PROT_WRITE,
++ MAP_ANON, 0, 0);
++ dtable = *dtableP;
++ *dtablepoly = MACH_MSG_TYPE_COPY_SEND;
++ *dtablelen = 3;
++ dtable[0] = dtable[1] = dtable[2] = console;
++
++ /* Initial ports. */
++ 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;
++ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
++ portarray[INIT_PORT_BOOTSTRAP] = bootstrap_port; /* use the same port */
++ *portarraypoly = MACH_MSG_TYPE_COPY_SEND;
++
++ /* Initial ints. */
++ *intarrayP = NULL;
++ *intarraylen = 0;
++
++ return 0;
++}
++
++boolean_t
++exec_startup_get_info_demuxer (mach_msg_header_t *request,
++ mach_msg_header_t *reply)
++{
++ extern boolean_t exec_startup_server (mach_msg_header_t *,
++ mach_msg_header_t *);
++ if (request->msgh_id != 30500) /* XXX hardcoded msgh_id */
++ {
++ /* Return MIG_BAD_ID. */
++ mig_reply_setup (request, reply);
++ return FALSE;
++ }
++ return exec_startup_server (request, reply);
++}
++
++error_t
++service_exec_startup_request (mach_port_t bootstrap,
++ char *argz,
++ size_t argz_len,
++ mach_msg_timeout_t timeout)
++{
++ error_t err;
++
++ if (! MACH_PORT_VALID (bootstrap))
++ return EINVAL;
++
++ exec_startup_get_info_args.bootstrap_port = bootstrap;
++ exec_startup_get_info_args.argz = argz;
++ exec_startup_get_info_args.argz_len = argz_len;
++
++ err = mach_msg_server_timeout_once (exec_startup_get_info_demuxer,
++ 0, bootstrap,
++ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
++ timeout);
++ if (err != MACH_MSG_SUCCESS)
++ return err;
++
++ return 0;
++}
++
++pointer
++do_handle_exec_startup (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("handle-exec-startup");
++ SC_ARG (sc, mach_port_t, bootstrap, number, args);
++ SC_ARG (sc, pointer, arguments, list, args);
++ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
++ SC_ARGS_DONE (sc);
++ char *argz = NULL;
++ size_t argz_len = 0;
++ ffi_list2argz (sc, &argz, &argz_len, arguments);
++ err = service_exec_startup_request (bootstrap, argz, argz_len, timeout);
++ free (argz);
++ SC_RETURN (sc);
++}
+diff --git a/bootshell/ffi.c b/bootshell/ffi.c
+new file mode 100644
+index 0000000..3b3088a
+--- /dev/null
++++ b/bootshell/ffi.c
+@@ -0,0 +1,1215 @@
++/* Mach, Hurd, and libc primitives for the Scheme environment.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <argz.h>
++#include <assert.h>
++#include <ctype.h>
++#include <device/device.h>
++#include <errno.h>
++#include <error.h>
++#include <fcntl.h>
++#include <hurd.h>
++#include <hurd/fsys.h>
++#include <mach.h>
++#include <mach/gnumach.h>
++#include <sys/reboot.h>
++#include <sys/mman.h>
++#include <string.h>
++#include <unistd.h>
++
++#if HAVE_LIBREADLINE
++#include <readline/readline.h>
++#include <readline/history.h>
++#endif
++
++#include "bootshell.h"
++#include "ffi.h"
++
++#include "fsys.h"
++#include "startup.h"
++
++static mach_port_t
++copy_send_right (mach_port_t right)
++{
++ error_t err;
++ if (! MACH_PORT_VALID (right))
++ return right;
++
++ err = mach_port_insert_right (mach_task_self (),
++ right,
++ right,
++ MACH_MSG_TYPE_COPY_SEND);
++ if (err)
++ {
++ error (0, err, "mach_port_insert_right");
++ return MACH_PORT_NULL;
++ }
++
++ return right;
++}
++
++
++pointer
++do_logand (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("logand");
++ unsigned int acc = ~0;
++ while (args != sc->NIL)
++ {
++ SC_ARG (sc, unsigned int, v, number, args);
++ acc &= v;
++ }
++ SC_RETURN_INT (sc, acc);
++}
++
++pointer
++do_logior (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("logior");
++ unsigned int acc = 0;
++ while (args != sc->NIL)
++ {
++ SC_ARG (sc, unsigned int, v, number, args);
++ acc |= v;
++ }
++ SC_RETURN_INT (sc, acc);
++}
++
++pointer
++do_logxor (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("logxor");
++ unsigned int acc = 0;
++ while (args != sc->NIL)
++ {
++ SC_ARG (sc, unsigned int, v, number, args);
++ acc ^= v;
++ }
++ SC_RETURN_INT (sc, acc);
++}
++
++pointer
++do_lognot (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("lognot");
++ SC_ARG (sc, unsigned int, v, number, args);
++ SC_ARGS_DONE (sc);
++ SC_RETURN_INT (sc, ~v);
++}
++
++
++
++
++int use_libreadline;
++
++/* Read a string, and return a pointer to it. Returns NULL on EOF. */
++char *
++rl_gets (const char *prompt)
++{
++ static char *line = NULL;
++ free (line);
++
++#if HAVE_LIBREADLINE
++ if (use_libreadline)
++ {
++ line = readline (prompt);
++ if (line && *line)
++ add_history (line);
++ }
++ else
++#endif
++ {
++ size_t max_size = 0xff;
++ printf ("%s", prompt);
++ fflush (stdout);
++ line = malloc (max_size);
++ if (line != NULL)
++ fgets (line, max_size, stdin);
++ }
++
++ /* Strip trailing whitespace. */
++ if (line && strlen (line) > 0)
++ for (char *p = &line[strlen (line) - 1]; isspace (*p); p--)
++ *p = 0;
++
++ return line;
++}
++
++pointer
++do_enable_readline (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("enable-readline");
++ SC_ARGS_DONE (sc);
++ use_libreadline = 1;
++ SC_RETURN (sc);
++}
++
++pointer
++do_prompt (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("prompt");
++ SC_ARG (sc, char *, prompt, string, args);
++ SC_ARGS_DONE (sc);
++ const char *line = rl_gets (prompt);
++ ffi_update (sc);
++ if (! line)
++ SC_RETURN_POINTER (sc, sc->EOF_OBJ);
++
++ SC_RETURN_STRING (sc, line);
++}
++
++#define is_false(p) ((p) == sc->F)
++
++pointer
++do_host_reboot (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("host-reboot");
++ SC_ARG (sc, mach_port_t, host_priv, number, args);
++ SC_ARG (sc, int, flags, number, args);
++ SC_ARGS_DONE (sc);
++ err = host_reboot (host_priv, flags);
++ SC_RETURN (sc);
++}
++
++pointer
++do_task_create (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-create");
++ SC_ARG (sc, task_t, parent, number, args);
++ SC_ARG (sc, boolean_t, inherit_memory, number, args);
++ SC_ARGS_DONE (sc);
++ task_t task;
++ err = task_create (parent, inherit_memory, &task);
++ SC_RETURN_INT (sc, task);
++}
++
++pointer
++do_task_suspend (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-suspend");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ err = task_suspend (task);
++ SC_RETURN (sc);
++}
++
++pointer
++do_task_resume (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-resume");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ err = task_resume (task);
++ SC_RETURN (sc);
++}
++
++pointer
++do_task_terminate (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-terminate");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ err = task_terminate (task);
++ SC_RETURN (sc);
++}
++
++pointer
++do_task_set_name (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-terminate");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, char *, name, path, args);
++ SC_ARGS_DONE (sc);
++ err = task_set_name (task, name);
++ SC_RETURN (sc);
++}
++
++pointer
++do_vm_set_default_memory_manager (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-terminate");
++ SC_ARG (sc, mach_port_t, host_priv, number, args);
++ SC_ARG (sc, mach_port_t, defpager, number, args);
++ SC_ARGS_DONE (sc);
++ err = vm_set_default_memory_manager (host_priv, &defpager);
++ SC_RETURN_INT (sc, defpager);
++}
++
++
++pointer
++do_sleep (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("sleep");
++ SC_ARG (sc, unsigned int, seconds, number, args);
++ SC_ARGS_DONE (sc);
++ sleep (seconds);
++ ffi_update (sc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_usleep (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("usleep");
++ SC_ARG (sc, useconds_t, microseconds, number, args);
++ SC_ARGS_DONE (sc);
++ usleep (microseconds);
++ ffi_update (sc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_file_name_lookup (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("file-name-lookup");
++ SC_ARG (sc, char *, name, path, args);
++ SC_ARG (sc, int, flags, number, args);
++ SC_ARG (sc, mode_t, mode, number, args);
++ SC_ARGS_DONE (sc);
++ file_t file = file_name_lookup (name, flags, mode);
++ if (! MACH_PORT_VALID (file))
++ SC_RETURN_ERR (sc, errno);
++ SC_RETURN_INT (sc, file);
++}
++
++pointer
++do_chdir (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("chdir");
++ SC_ARG (sc, char *, name, path, args);
++ SC_ARGS_DONE (sc);
++ if (chdir (name))
++ SC_RETURN_ERR (sc, errno);
++ SC_RETURN (sc);
++}
++
++pointer
++do_strerror (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("strerror");
++ SC_ARG (sc, int, error, number, args);
++ SC_ARGS_DONE (sc);
++ char *s, buf[128];
++ s = strerror_r (error, buf, sizeof buf);
++ SC_RETURN_STRING (sc, s);
++}
++
++pointer
++do_getproc (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("getproc");
++ SC_ARGS_DONE (sc);
++ SC_RETURN_INT (sc, getproc ());
++}
++
++pointer
++do_getcwd (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("getcwd");
++ SC_ARGS_DONE (sc);
++ char *cwd = get_current_dir_name ();
++ if (cwd == NULL)
++ SC_RETURN_ERR (sc, errno);
++ SC_RETURN_STRING (sc, cwd);
++}
++
++pointer
++do_mach_port_allocate (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("mach-port-allocate");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, mach_port_t, right, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t name;
++ err = mach_port_allocate (task, right, &name);
++ SC_RETURN_INT (sc, name);
++}
++
++pointer
++do_mach_port_deallocate (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("mach-port-deallocate");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, mach_port_t, right, number, args);
++ SC_ARGS_DONE (sc);
++ err = mach_port_deallocate (task, right);
++ SC_RETURN (sc);
++}
++
++pointer
++do_mach_port_destroy (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("mach-port-destroy");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, mach_port_t, right, number, args);
++ SC_ARGS_DONE (sc);
++ err = mach_port_destroy (task, right);
++ SC_RETURN (sc);
++}
++
++pointer
++do_mach_port_insert_right (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("mach-port-insert-right");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, mach_port_t, name, number, args);
++ SC_ARG (sc, mach_port_t, right, number, args);
++ SC_ARG (sc, mach_msg_type_name_t, right_type, number, args);
++ SC_ARGS_DONE (sc);
++ err = mach_port_insert_right (task, name, right, right_type);
++ SC_RETURN_INT (sc, right);
++}
++
++pointer
++do_task_get_special_port (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-get-special-port");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, int, which, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t special_port;
++ err = task_get_special_port (task, which, &special_port);
++ SC_RETURN_INT (sc, special_port);
++}
++
++pointer
++do_task_set_special_port (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("task-set-special-port");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, int, which, number, args);
++ SC_ARG (sc, mach_port_t, special_port, number, args);
++ SC_ARGS_DONE (sc);
++ err = task_set_special_port (task, which, special_port);
++ SC_RETURN (sc);
++}
++
++pointer
++do_device_open (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("device-open");
++ SC_ARG (sc, mach_port_t, master, number, args);
++ SC_ARG (sc, int, flags, number, args);
++ SC_ARG (sc, char *, name, path, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t device;
++ err = device_open (master, flags, name, &device);
++ SC_RETURN_INT (sc, device);
++}
++
++/* Hurd functions. */
++pointer
++do_bind_root (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("_bind-root");
++ SC_ARG (sc, mach_port_t, control, number, args);
++ SC_ARG (sc, file_t, dotdot_node, number, args);
++ SC_ARGS_DONE (sc);
++ if (! MACH_PORT_VALID (control)
++ || ! MACH_PORT_VALID (dotdot_node))
++ SC_RETURN_ERR (sc, EINVAL);
++
++ if (_hurd_ports)
++ SC_RETURN_ERR (sc, EPERM); /* XXX */
++
++ uid_t uids[1] = { 0 };
++ size_t uids_len = 1;
++ gid_t gids[1] = { 0 };
++ size_t gids_len = 1;
++
++ retry_type retry;
++ char retryname[1024]; /* XXX */
++ file_t root;
++ err = fsys_getroot (control,
++ dotdot_node,
++ MACH_MSG_TYPE_MAKE_SEND,
++ uids, uids_len,
++ gids, gids_len,
++ (O_READ|O_EXEC),
++ &retry,
++ retryname,
++ &root);
++ if (err)
++ SC_RETURN (sc);
++
++ // XXX check root
++ portarray_template[INIT_PORT_CRDIR] = root;
++ portarray_template[INIT_PORT_CWDIR] = root;
++
++ err = mach_port_mod_refs (mach_task_self (),
++ root, MACH_PORT_RIGHT_SEND, +2);
++ assert_perror (err);
++
++ /* 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;
++ portarray = mmap (0, INIT_PORT_MAX * sizeof *portarray,
++ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
++ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
++ /* XXX intarray */
++ _hurd_init (0, global_argv, portarray, INIT_PORT_MAX, NULL, 0);
++ SC_RETURN (sc);
++}
++
++pointer
++do_bind_proc (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("bind-proc");
++ SC_ARG (sc, mach_port_t, procserver, number, args);
++ SC_ARGS_DONE (sc);
++ /* Give the library our proc server port. */
++ err = mach_port_mod_refs (mach_task_self (),
++ procserver, MACH_PORT_RIGHT_SEND, +1);
++ assert_perror (err);
++ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver);
++
++ /* When we called _hurd_init, the proc server wasn't around. */
++ _hurd_new_proc_init (global_argv, NULL, 0); /* XXX intarray */
++ SC_RETURN (sc);
++}
++
++pointer
++do_bind_auth (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("bind-auth");
++ SC_ARG (sc, mach_port_t, authserver, number, args);
++ SC_ARGS_DONE (sc);
++ /* Give the library our auth server port. */
++ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
++ portarray_template[INIT_PORT_AUTH] = authserver;
++ err = mach_port_mod_refs (mach_task_self (),
++ authserver, MACH_PORT_RIGHT_SEND, +2);
++ assert_perror (err);
++ SC_RETURN (sc);
++}
++
++pointer
++do_bind_term (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("bind-term");
++ SC_ARG (sc, mach_port_t, term, number, args);
++ SC_ARGS_DONE (sc);
++ int new_fd0, new_fd1, new_fd2;
++ FILE *new_stdin, *new_stdout, *new_stderr;
++
++ new_fd0 = openport (copy_send_right (term), O_RDONLY);
++ if (new_fd0 < 0)
++ SC_RETURN_ERR (sc, errno);
++
++ new_fd1 = openport (copy_send_right (term), O_WRONLY);
++ if (new_fd1 < 0)
++ SC_RETURN_ERR (sc, errno);
++
++ new_fd2 = openport (copy_send_right (term), O_WRONLY);
++ if (new_fd2 < 0)
++ SC_RETURN_ERR (sc, errno);
++
++ dup2 (new_fd0, 0);
++ close (new_fd0);
++ dup2 (new_fd1, 1);
++ close (new_fd1);
++ dup2 (new_fd2, 2);
++ close (new_fd2);
++
++ // XXX proper error handling, restore all state on error
++
++ new_stdin = fdopen (0, "r");
++ if (new_stdin == NULL)
++ SC_RETURN_ERR (sc, errno);
++
++ new_stdout = fdopen (1, "w");
++ if (new_stdout == NULL)
++ SC_RETURN_ERR (sc, errno);
++
++ new_stderr = fdopen (2, "w");
++ if (new_stderr == NULL)
++ SC_RETURN_ERR (sc, errno);
++
++ fclose (stdin);
++ fclose (stdout);
++ fclose (stderr);
++ stdin = new_stdin;
++ stdout = new_stdout;
++ stderr = new_stderr;
++
++ scheme_set_input_port_file (sc, stdin);
++ scheme_set_output_port_file (sc, stdout);
++
++ SC_RETURN (sc);
++}
++
++pointer
++do_fsys_init (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("fsys-init!");
++ SC_ARG (sc, mach_port_t, fsys, number, args);
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, mach_port_t, auth, number, args);
++ SC_ARGS_DONE (sc);
++ err = fsys_init (fsys, proc, MACH_MSG_TYPE_COPY_SEND, auth);
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_task2proc (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc-task2proc");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t result;
++ err = proc_task2proc (proc, task, &result);
++ SC_RETURN_INT (sc, result);
++}
++
++pointer
++do_proc_task2pid (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc-task2pid");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ pid_t pid;
++ err = proc_task2pid (proc, task, &pid);
++ SC_RETURN_INT (sc, pid);
++}
++
++pointer
++do_proc_pid2task (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc-pid2task");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, pid_t, pid, number, args);
++ SC_ARGS_DONE (sc);
++ task_t task;
++ err = proc_pid2task (proc, pid, &task);
++ SC_RETURN_INT (sc, task);
++}
++
++pointer
++do_proc_wait (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc-wait");
++ SC_ARG (sc, process_t, proc, number, args);
++ SC_ARG (sc, pid_t, pid, number, args);
++ SC_ARG (sc, int, options, number, args);
++ SC_ARGS_DONE (sc);
++ int status, sigcode;
++ rusage_t rusage; // XXX
++ pid_t pid_status;
++ err = proc_wait (proc, pid, options,
++ &status, &sigcode, &rusage, &pid_status);
++#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
++ SC_RETURN_POINTER (sc, IMC (status,
++ IMC (sigcode,
++ IMC (pid_status, sc->NIL))));
++#undef IMC
++}
++
++pointer
++do_proc_mark_exec (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc-mark-exec!");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_mark_exec (proc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_mark_important (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->mark-important!");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_mark_important (proc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_child (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->proc->child!");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, mach_port_t, child, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_child (proc, child);
++ if (err) error (0, err, "proc_child");
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_set_init_task (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->task->set-init-task!");
++ SC_ARG (sc, mach_port_t, proc, number, args);
++ SC_ARG (sc, mach_port_t, task, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_set_init_task (proc, task);
++ SC_RETURN (sc);
++}
++
++/* Set up the initial value of the standard exec data. */
++/* XXX: Provide primitives and implement in Scheme. */
++pointer
++do_proc_auth_set_std_execdata (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->auth->set-std-execdata!");
++ SC_ARG (sc, process_t, proc, number, args);
++ SC_ARG (sc, auth_t, auth, number, args);
++ SC_ARGS_DONE (sc);
++
++ auth_t nullauth;
++ mach_port_t pt;
++ mach_port_t ref;
++ mach_port_t *std_port_array;
++ int *std_int_array;
++ int i;
++
++ std_port_array = alloca (sizeof (mach_port_t) * INIT_PORT_MAX);
++ memset (std_port_array, 0, sizeof(mach_port_t) * INIT_PORT_MAX);
++ std_int_array = alloca (sizeof (int) * INIT_INT_MAX);
++ memset (std_int_array, 0, sizeof(int) * INIT_INT_MAX);
++
++ err = auth_makeauth (auth,
++ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
++ NULL, 0,
++ NULL, 0,
++ NULL, 0,
++ NULL, 0,
++ &nullauth);
++ if (err)
++ goto out;
++
++ /* MAKE_SEND is safe in these transactions because we destroy REF
++ ourselves each time. */
++ pt = getcwdir ();
++ ref = mach_reply_port ();
++ err = io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
++ if (err)
++ goto out;
++
++ err = auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
++ &std_port_array[INIT_PORT_CWDIR]);
++ if (err)
++ goto out;
++
++ mach_port_destroy (mach_task_self (), ref);
++ mach_port_deallocate (mach_task_self (), pt);
++
++ pt = getcrdir ();
++ ref = mach_reply_port ();
++ err = io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
++ if (err)
++ goto out;
++
++ err = auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
++ &std_port_array[INIT_PORT_CRDIR]);
++ if (err)
++ goto out;
++
++ mach_port_destroy (mach_task_self (), ref);
++ mach_port_deallocate (mach_task_self (), pt);
++
++ std_port_array[INIT_PORT_AUTH] = nullauth;
++ std_int_array[INIT_UMASK] = CMASK;
++
++ err = proc_setexecdata (proc, std_port_array,
++ MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
++ std_int_array, INIT_INT_MAX);
++
++ out:
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ if (MACH_PORT_VALID (std_port_array[i]))
++ {
++ error_t err;
++ err = mach_port_deallocate (mach_task_self (), std_port_array[i]);
++ assert_perror (err);
++ }
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_make_login_coll (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->make-login-coll!");
++ SC_ARG (sc, process_t, proc, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_make_login_coll (proc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_proc_setsid (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("proc->setsid!");
++ SC_ARG (sc, process_t, proc, number, args);
++ SC_ARGS_DONE (sc);
++ err = proc_setsid (proc);
++ SC_RETURN (sc);
++}
++
++pointer
++do_tcsetpgrp (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("tcsetpgrp");
++ SC_ARG (sc, int, fd, number, args);
++ SC_ARG (sc, pid_t, pgrp, number, args);
++ SC_ARGS_DONE (sc);
++ tcsetpgrp (fd, pgrp);
++ SC_RETURN (sc);
++}
++
++pointer
++ffi_argz2list (scheme *sc, const char *argz, size_t argz_len, const char *entry)
++{
++ entry = argz_next (argz, argz_len, entry);
++ if (argz == NULL || argz_len == 0 || entry == NULL)
++ return sc->NIL;
++ return _cons (sc,
++ mk_string (sc, entry),
++ ffi_argz2list (sc, argz, argz_len, entry),
++ 1);
++}
++
++void
++ffi_list2argz (scheme *sc, char **argz, size_t *argz_len, pointer list)
++{
++ for (; sc->vptr->is_pair (list); list = sc->vptr->pair_cdr (list))
++ {
++ char *v;
++ if (sc->vptr->is_string (sc->vptr->pair_car (list)))
++ v = sc->vptr->string_value (sc->vptr->pair_car (list));
++ else if (sc->vptr->is_symbol (sc->vptr->pair_car (list)))
++ v = sc->vptr->symname (sc->vptr->pair_car (list));
++ else
++ continue; // XXX this just silently drops values
++ argz_add (argz, argz_len, v);
++ }
++}
++
++pointer
++do_file_set_translator (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("file-set-translator");
++ SC_ARG (sc, file_t, node, number, args);
++ SC_ARG (sc, int, passive_flags, number, args);
++ SC_ARG (sc, int, active_flags, number, args);
++ SC_ARG (sc, int, goaway_flags, number, args);
++ SC_ARG (sc, pointer, arguments, list, args);
++ char *argz = NULL;
++ size_t argz_len = 0;
++ ffi_list2argz (sc, &argz, &argz_len, arguments);
++ SC_ARG (sc, mach_port_t, active_control, number, args);
++ SC_ARG (sc, mach_msg_type_name_t, active_controlPoly, number, args);
++ SC_ARGS_DONE (sc);
++ err = file_set_translator (node,
++ passive_flags, active_flags, goaway_flags,
++ argz, argz_len,
++ active_control, active_controlPoly);
++ SC_RETURN (sc);
++}
++
++// XXX
++pointer
++do__exec (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("_exec");
++ process_t proc, child_proc = MACH_PORT_NULL;
++ mach_port_t dtable[STDERR_FILENO+1];
++ mach_port_t portarray[INIT_PORT_MAX];
++ int default_ints[INIT_INT_MAX];
++ char *argz = NULL;
++ size_t argz_len = 0;
++ int i;
++
++ SC_ARG (sc, file_t, file, number, args);
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, pointer, arguments, list, args);
++ ffi_list2argz (sc, &argz, &argz_len, arguments);
++ SC_ARG (sc, mach_port_t, bootstrap, number, args);
++ SC_ARGS_DONE (sc);
++
++ proc = getproc ();
++ if (MACH_PORT_VALID (proc))
++ {
++ err = proc_task2proc (proc, task, &child_proc);
++ mach_port_deallocate (mach_task_self (), proc);
++ if (err)
++ SC_RETURN (sc);
++ }
++
++ dtable[STDIN_FILENO] = getdport (STDIN_FILENO);
++ dtable[STDOUT_FILENO] = getdport (STDOUT_FILENO);
++ dtable[STDERR_FILENO] = getdport (STDERR_FILENO);
++
++ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ copy_send_right (portarray[i]);
++
++ portarray[INIT_PORT_CWDIR] = getcwdir ();
++ portarray[INIT_PORT_CRDIR] = getcrdir ();
++ portarray[INIT_PORT_PROC] = child_proc;
++ portarray[INIT_PORT_BOOTSTRAP] = copy_send_right (bootstrap);
++
++ memset (default_ints, 0, INIT_INT_MAX * sizeof *default_ints);
++ /* All programs we start should ignore job control stop signals.
++ That way Posix.1 B.2.2.2 is satisfied where it says that programs
++ not run under job control shells are protected. */
++ default_ints[INIT_SIGIGN] = (sigmask (SIGTSTP)
++ | sigmask (SIGTTIN)
++ | sigmask (SIGTTOU));
++
++ err = task_set_name (task, argz);
++ if (err)
++ {
++ error (0, err, "task_set_name");
++ goto lose;
++ }
++
++ err = file_exec (file, task, 0,
++ argz, argz_len,
++ NULL, 0, /* env, env_len */
++ dtable, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO+1,
++ portarray, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
++ default_ints, INIT_INT_MAX,
++ NULL, 0, NULL, 0);
++
++ lose:
++ for (i = 0; i < STDERR_FILENO+1; i++)
++ if (MACH_PORT_VALID (dtable[i]))
++ {
++ error_t err = mach_port_deallocate (mach_task_self (), dtable[i]);
++ assert_perror (err);
++ }
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ if (MACH_PORT_VALID (portarray[i]))
++ {
++ error_t err = mach_port_deallocate (mach_task_self (), portarray[i]);
++ assert_perror (err);
++ }
++
++ SC_RETURN_INT (sc, task);
++}
++
++pointer
++do_file_get_fs_options (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("file-get-fs-options");
++ SC_ARG (sc, file_t, node, number, args);
++ SC_ARGS_DONE (sc);
++ char *argz = NULL;
++ size_t argz_len = 0;
++ pointer result;
++ err = file_get_fs_options (node, &argz, &argz_len);
++ if (err)
++ SC_RETURN (sc);
++ result = ffi_argz2list (sc, argz, argz_len, NULL);
++ vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
++ SC_RETURN_POINTER (sc, result);
++}
++
++const char *
++schemify_name (const char *s, int macro)
++{
++ char *n = strdup (s), *p;
++ if (n == NULL)
++ return s;
++ for (p = n; *p; p++)
++ {
++ *p = (char) tolower (*p);
++ /* We convert _ to - in identifiers. We allow, however, for
++ function names to start with a leading _. The functions in
++ this namespace are not yet finalized and might change or
++ vanish without warning. Use them with care. */
++ if (! macro
++ && p != n
++ && *p == '_')
++ *p = '-';
++ }
++ return n;
++}
++
++void
++ffi_update (scheme *sc)
++{
++ // XXX nothng
++}
++
++void
++ffi_init (scheme *sc)
++{
++ char *version;
++ asprintf (&version, "%s/%s", argp_program_version, "TinyScheme 1.41");
++ define_ (sc, "version", mk_string (sc, version));
++ free (version);
++
++ /* bitwise arithmetic */
++ define_function (sc, "logand", logand);
++ define_function (sc, "logior", logior);
++ define_function (sc, "logxor", logxor);
++ define_function (sc, "lognot", lognot);
++
++ /* Mach stuff. */
++ define_constant (sc, MACH_PORT_NULL);
++ define_constant (sc, MACH_PORT_DEAD); // XXX signedness
++
++ define_constant (sc, MACH_MSG_TYPE_UNSTRUCTURED);
++ define_constant (sc, MACH_MSG_TYPE_BIT);
++ define_constant (sc, MACH_MSG_TYPE_BOOLEAN);
++ define_constant (sc, MACH_MSG_TYPE_INTEGER_16);
++ define_constant (sc, MACH_MSG_TYPE_INTEGER_32);
++ define_constant (sc, MACH_MSG_TYPE_CHAR);
++ define_constant (sc, MACH_MSG_TYPE_BYTE);
++ define_constant (sc, MACH_MSG_TYPE_INTEGER_8);
++ define_constant (sc, MACH_MSG_TYPE_REAL);
++ define_constant (sc, MACH_MSG_TYPE_INTEGER_64);
++ define_constant (sc, MACH_MSG_TYPE_STRING);
++ define_constant (sc, MACH_MSG_TYPE_STRING_C);
++ define_constant (sc, MACH_MSG_TYPE_MOVE_RECEIVE);
++ define_constant (sc, MACH_MSG_TYPE_MOVE_SEND);
++ define_constant (sc, MACH_MSG_TYPE_MOVE_SEND_ONCE);
++ define_constant (sc, MACH_MSG_TYPE_COPY_SEND);
++ define_constant (sc, MACH_MSG_TYPE_MAKE_SEND);
++ define_constant (sc, MACH_MSG_TYPE_MAKE_SEND_ONCE);
++ define_constant (sc, MACH_MSG_TYPE_PORT_NAME);
++ define_constant (sc, MACH_MSG_TYPE_PORT_RECEIVE);
++ define_constant (sc, MACH_MSG_TYPE_PORT_SEND);
++ define_constant (sc, MACH_MSG_TYPE_PORT_SEND_ONCE);
++ define_constant (sc, MACH_MSG_TYPE_PROTECTED_PAYLOAD);
++ define_constant (sc, MACH_MSG_TYPE_LAST);
++ define_constant (sc, MACH_MSG_TYPE_POLYMORPHIC);
++
++ define_constant (sc, MACH_PORT_RIGHT_SEND);
++ define_constant (sc, MACH_PORT_RIGHT_RECEIVE);
++ define_constant (sc, MACH_PORT_RIGHT_SEND_ONCE);
++ define_constant (sc, MACH_PORT_RIGHT_PORT_SET);
++ define_constant (sc, MACH_PORT_RIGHT_DEAD_NAME);
++ define_constant (sc, MACH_PORT_RIGHT_NUMBER);
++
++ define_constant (sc, KERN_SUCCESS);
++ define_constant (sc, KERN_INVALID_ADDRESS);
++ define_constant (sc, KERN_PROTECTION_FAILURE);
++ define_constant (sc, KERN_NO_SPACE);
++ define_constant (sc, KERN_INVALID_ARGUMENT);
++ define_constant (sc, KERN_FAILURE);
++ define_constant (sc, KERN_RESOURCE_SHORTAGE);
++ define_constant (sc, KERN_NOT_RECEIVER);
++ define_constant (sc, KERN_NO_ACCESS);
++ define_constant (sc, KERN_MEMORY_FAILURE);
++ define_constant (sc, KERN_MEMORY_ERROR);
++ define_constant (sc, KERN_NOT_IN_SET);
++ define_constant (sc, KERN_NAME_EXISTS);
++ define_constant (sc, KERN_ABORTED);
++ define_constant (sc, KERN_INVALID_NAME);
++ define_constant (sc, KERN_INVALID_TASK);
++ define_constant (sc, KERN_INVALID_RIGHT);
++ define_constant (sc, KERN_INVALID_VALUE);
++ define_constant (sc, KERN_UREFS_OVERFLOW);
++ define_constant (sc, KERN_INVALID_CAPABILITY);
++ define_constant (sc, KERN_RIGHT_EXISTS);
++ define_constant (sc, KERN_INVALID_HOST);
++ define_constant (sc, KERN_MEMORY_PRESENT);
++ define_constant (sc, KERN_WRITE_PROTECTION_FAILURE);
++ define_constant (sc, KERN_TERMINATED);
++ define_constant (sc, MACH_MSG_SUCCESS);
++ define_constant (sc, MACH_MSG_MASK);
++ define_constant (sc, MACH_MSG_IPC_SPACE);
++ define_constant (sc, MACH_MSG_VM_SPACE);
++ define_constant (sc, MACH_MSG_IPC_KERNEL);
++ define_constant (sc, MACH_MSG_VM_KERNEL);
++ define_constant (sc, MACH_SEND_IN_PROGRESS);
++ define_constant (sc, MACH_SEND_INVALID_DATA);
++ define_constant (sc, MACH_SEND_INVALID_DEST);
++ define_constant (sc, MACH_SEND_TIMED_OUT);
++ define_constant (sc, MACH_SEND_WILL_NOTIFY);
++ define_constant (sc, MACH_SEND_NOTIFY_IN_PROGRESS);
++ define_constant (sc, MACH_SEND_INTERRUPTED);
++ define_constant (sc, MACH_SEND_MSG_TOO_SMALL);
++ define_constant (sc, MACH_SEND_INVALID_REPLY);
++ define_constant (sc, MACH_SEND_INVALID_RIGHT);
++ define_constant (sc, MACH_SEND_INVALID_NOTIFY);
++ define_constant (sc, MACH_SEND_INVALID_MEMORY);
++ define_constant (sc, MACH_SEND_NO_BUFFER);
++ define_constant (sc, MACH_SEND_NO_NOTIFY);
++ define_constant (sc, MACH_SEND_INVALID_TYPE);
++ define_constant (sc, MACH_SEND_INVALID_HEADER);
++ define_constant (sc, MACH_RCV_IN_PROGRESS);
++ define_constant (sc, MACH_RCV_INVALID_NAME);
++ define_constant (sc, MACH_RCV_TIMED_OUT);
++ define_constant (sc, MACH_RCV_TOO_LARGE);
++ define_constant (sc, MACH_RCV_INTERRUPTED);
++ define_constant (sc, MACH_RCV_PORT_CHANGED);
++ define_constant (sc, MACH_RCV_INVALID_NOTIFY);
++ define_constant (sc, MACH_RCV_INVALID_DATA);
++ define_constant (sc, MACH_RCV_PORT_DIED);
++ define_constant (sc, MACH_RCV_IN_SET);
++ define_constant (sc, MACH_RCV_HEADER_ERROR);
++ define_constant (sc, MACH_RCV_BODY_ERROR);
++
++ define_constant (sc, TASK_KERNEL_PORT);
++ define_constant (sc, TASK_EXCEPTION_PORT);
++ define_constant (sc, TASK_BOOTSTRAP_PORT);
++
++ define_constant (sc, RB_DEBUGGER);
++ define_constant (sc, RB_HALT);
++ define_constant (sc, RB_AUTOBOOT);
++
++ define_ (sc, "mach-task-self", mk_integer (sc, mach_task_self ()));
++
++ define_function (sc, "mach-port-allocate", mach_port_allocate);
++ define_function (sc, "mach-port-deallocate", mach_port_deallocate);
++ define_function (sc, "mach-port-destroy", mach_port_destroy);
++ //define_function (sc, "mach-port-get-refs", mach_port_get_refs);
++ //define_function (sc, "mach-port-mod-refs", mach_port_mod_refs);
++ define_function (sc, "mach-port-insert-right", mach_port_insert_right);
++ //define_function (sc, "mach-port-extract-right", mach_port_extract_right);
++
++ define_function (sc, "task-create", task_create);
++ define_function (sc, "task-suspend", task_suspend);
++ define_function (sc, "task-resume", task_resume);
++ define_function (sc, "task-terminate", task_terminate);
++ define_function (sc, "task-set-name", task_set_name);
++ define_function (sc, "task-get-special-port", task_get_special_port);
++ define_function (sc, "task-set-special-port", task_set_special_port);
++ define_function (sc, "host-reboot", host_reboot);
++
++ define_function (sc, "vm-set-default-memory-manager",
++ vm_set_default_memory_manager);
++
++ /* Device protocol. */
++ define_constant (sc, D_READ);
++ define_constant (sc, D_WRITE);
++ define_function (sc, "device-open", device_open);
++
++ /* Hurd stuff. */
++ define_constant (sc, EXEC_NEWTASK);
++ define_constant (sc, EXEC_SECURE);
++ define_constant (sc, EXEC_DEFAULTS);
++ define_constant (sc, EXEC_SIGTRAP);
++ define_constant (sc, EXEC_STACK_ARGS);
++ define_constant (sc, FS_TRANS_FORCE);
++ define_constant (sc, FS_TRANS_EXCL);
++ define_constant (sc, FS_TRANS_SET);
++ define_constant (sc, FS_TRANS_ORPHAN);
++ define_constant (sc, FS_RETRY_NORMAL);
++ define_constant (sc, FS_RETRY_REAUTH);
++ define_constant (sc, FS_RETRY_MAGICAL);
++ define_constant (sc, FSYS_GOAWAY_NOWAIT);
++ define_constant (sc, FSYS_GOAWAY_NOSYNC);
++ define_constant (sc, FSYS_GOAWAY_FORCE);
++ define_constant (sc, FSYS_GOAWAY_UNLINK);
++ define_constant (sc, FSYS_GOAWAY_RECURSE);
++ define_constant (sc, INIT_PORT_CWDIR);
++ define_constant (sc, INIT_PORT_CRDIR);
++ define_constant (sc, INIT_PORT_AUTH);
++ define_constant (sc, INIT_PORT_PROC);
++ define_constant (sc, INIT_PORT_CTTYID);
++ define_constant (sc, INIT_PORT_BOOTSTRAP);
++ define_constant (sc, INIT_PORT_MAX);
++ define_constant (sc, INIT_UMASK);
++ define_constant (sc, INIT_SIGMASK);
++ define_constant (sc, INIT_SIGIGN);
++ define_constant (sc, INIT_SIGPENDING);
++ define_constant (sc, INIT_TRACEMASK);
++ define_constant (sc, INIT_INT_MAX);
++
++ define_constant (sc, O_RDONLY);
++ define_constant (sc, O_WRONLY);
++ define_constant (sc, O_RDWR);
++ define_constant (sc, O_EXEC);
++ define_constant (sc, O_CREAT);
++ define_constant (sc, O_NOTRANS);
++
++ define_variable (sc, exception_port);
++ define_variable (sc, rootnode);
++
++ /* glibc. */
++ define_function (sc, "sleep", sleep);
++ define_function (sc, "usleep", usleep);
++ define_function (sc, "getcwd", getcwd);
++ define_function (sc, "chdir", chdir);
++ define_function (sc, "strerror", strerror);
++ define_function (sc, "getproc", getproc);
++
++ /* Boot process */
++ define_function (sc, "bind-root", bind_root);
++ define_function (sc, "bind-proc", bind_proc);
++ define_function (sc, "bind-auth", bind_auth);
++ define_function (sc, "bind-term", bind_term);
++ define_function (sc, "fsys-init", fsys_init);
++
++ /* Early bootstrap protocols. */
++ define_function (sc, "handle-startup-procinit", handle_startup_procinit);
++ define_function (sc, "handle-startup-authinit", handle_startup_authinit);
++ define_function (sc, "startup-procinit-reply", startup_procinit_reply);
++ define_function (sc, "startup-authinit-reply", startup_authinit_reply);
++
++ define_function (sc, "startup-essential-task", startup_essential_task);
++ define_function (sc, "startup-request-notification",
++ startup_request_notification);
++ define_function (sc, "startup-reboot", startup_reboot);
++
++ /* Process and translator startup. */
++ define_function (sc, "handle-exec-startup", handle_exec_startup);
++ define_function (sc, "handle-fsys-startup", handle_fsys_startup);
++
++ /* Hurd fsys protocol. */
++ define_function (sc, "fsys-set-options", fsys_set_options);
++ define_function (sc, "fsys-get-options", fsys_get_options);
++
++ /* Hurd fs API */
++ define_function (sc, "file-name-lookup", file_name_lookup);
++ define_function (sc, "file-set-translator", file_set_translator);
++ define_function (sc, "file-get-fs-options", file_get_fs_options);
++
++ /* Hurd process API */
++ define_function (sc, "proc-wait!", proc_wait);
++ define_function (sc, "proc->task->proc", proc_task2proc);
++ define_function (sc, "proc->task->pid", proc_task2pid);
++ define_function (sc, "proc->pid->task", proc_pid2task);
++ define_function (sc, "proc->mark-important!", proc_mark_important);
++ define_function (sc, "proc->mark-exec!", proc_mark_exec);
++ define_function (sc, "proc->task->child!", proc_child);
++ define_function (sc, "proc->task->set-init-task!", proc_set_init_task);
++ define_function (sc, "proc->make-login-coll!", proc_make_login_coll);
++ define_function (sc, "proc->setsid!", proc_setsid);
++
++ /* Terminal magic. */
++ define_function (sc, "tcsetpgrp", tcsetpgrp);
++
++ /* Hurd hacks. */
++ define_function (sc, "frob-task", frob_task);
++ define_function (sc, "elf-exec", elf_exec);
++ define_function (sc, "_exec", _exec);
++ define_function (sc, "start-handling-early-startup",
++ start_handling_early_startup);
++ define_function (sc, "get-essential-tasks", get_essential_tasks);
++ define_function (sc, "get-registered-tasks", get_registered_tasks);
++ define_function (sc, "proc->auth->set-std-execdata!",
++ proc_auth_set_std_execdata);
++
++ /* User interface. */
++ define_function (sc, "enable-readline", enable_readline);
++ define_function (sc, "prompt", prompt);
++
++ /* XXX */
++ ffi_update (sc);
++}
+diff --git a/bootshell/ffi.h b/bootshell/ffi.h
+new file mode 100644
+index 0000000..f5d0ac6
+--- /dev/null
++++ b/bootshell/ffi.h
+@@ -0,0 +1,170 @@
++/* Convenience functions and macros for the foreign function interface.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _HURD_BOOTSHELL_FFI_H
++#define _HURD_BOOTSHELL_FFI_H
++
++#include <mach.h>
++#include <mach/message.h>
++
++#include "scheme.h"
++#include "scheme-private.h"
++
++// XXX drop name
++#define SC_FFI_PROLOG(NAME) \
++ const char *__ffi_name __attribute__ ((unused)) = NAME; \
++ unsigned int __ffi_arg_index __attribute__ ((unused)) = 1; \
++ error_t err = 0; \
++
++#define CONVERSION_number(SC, X) ivalue
++#define CONVERSION_string(SC, X) string_value
++#define CONVERSION_list(SC, X)
++#define CONVERSION_path(SC, X) ((SC)->vptr->is_string (X) \
++ ? (SC)->vptr->string_value \
++ : (SC)->vptr->symname)
++
++#define IS_A_number(SC, X) (SC)->vptr->is_number (X)
++#define IS_A_string(SC, X) (SC)->vptr->is_string (X)
++#define IS_A_list(SC, X) (SC)->vptr->is_list (SC, X)
++#define IS_A_path(SC, X) ((SC)->vptr->is_string (X) \
++ || (SC)->vptr->is_symbol (X))
++
++// XXX proper error handling
++#define SC_ARG(SC, CTYPE, TARGET, WANT, ARGS) \
++ if ((ARGS) == (SC)->NIL) { \
++ fprintf (stderr, "Error: %s: too few arguments: " \
++ "want " #TARGET "("#WANT"/"#CTYPE")\n", __ffi_name); \
++ return (SC)->NIL; \
++ } \
++ if (! IS_A_##WANT ((SC), pair_car (ARGS))) { \
++ fprintf (stderr, "Error: %s: argument %d must be: " \
++ #WANT "\n", __ffi_name, __ffi_arg_index); \
++ return (SC)->NIL; \
++ } \
++ CTYPE TARGET = CONVERSION_##WANT (SC, pair_car (ARGS)) (pair_car (ARGS)); \
++ ARGS = pair_cdr (ARGS); \
++ __ffi_arg_index += 1; \
++
++#define SC_ARGS_DONE(SC) \
++ /* XXX */
++
++#define SC_RETURN_ERR(SC, ERR) \
++ return _cons ((SC), mk_integer ((SC), (ERR)), (SC)->NIL, 1)
++
++#define SC_RETURN(SC) SC_RETURN_ERR (SC, err)
++
++#define SC_RETURN_POINTER(SC, X) \
++ return _cons ((SC), mk_integer ((SC), err), \
++ _cons ((SC), (X), (SC)->NIL, 1), 1)
++#define SC_RETURN_INT(SC, X) \
++ SC_RETURN_POINTER ((SC), mk_integer ((SC), (X)))
++#define SC_RETURN_STRING(SC, X) \
++ SC_RETURN_POINTER ((SC), mk_string ((SC), (X)))
++
++const char *schemify_name (const char *s, int macro);
++
++#define define_function(S, P, F) \
++ ({ \
++ char _sc_buf[256]; \
++ scheme_define ((S), \
++ (S)->global_env, \
++ mk_symbol ((S), schemify_name ("_" #F, 0)), \
++ mk_foreign_func ((S), (do_##F))); \
++ snprintf (_sc_buf, sizeof _sc_buf, \
++ "(define (%1$s . a) (ffi-apply \"%1$s\" %2$s a))", \
++ P, schemify_name ("_" #F, 0)); \
++ scheme_load_string (S, _sc_buf); \
++ })
++
++#define define_constant(S, C) \
++ scheme_define ((S), \
++ (S)->global_env, \
++ mk_symbol ((S), schemify_name (#C, 1)), \
++ mk_integer ((S), (C)))
++
++#define define_(S, SYM, EXP) \
++ scheme_define ((S), (S)->global_env, mk_symbol ((S), (SYM)), EXP)
++
++#define define_variable(S, C) \
++ scheme_define ((S), \
++ (S)->global_env, \
++ mk_symbol ((S), schemify_name (#C, 0)), \
++ mk_integer ((S), (C)))
++
++#define define_variable_pointer(S, C, P) \
++ scheme_define ((S), \
++ (S)->global_env, \
++ mk_symbol ((S), schemify_name (#C, 0)), \
++ (P))
++
++#define define_variable_string(S, C) \
++ define_variable_pointer (S, C, (S)->vptr->mk_string (S, C ?: ""))
++
++/* A variant of scheme_load_string that does not require the string to
++ be zero-terminated. */
++void scheme_load_mem (scheme *, const char *, const char *, const char *);
++
++#define declare_embedded_script(X) \
++ extern char X##_size[] asm("_binary__"#X"_o_size"); \
++ extern char X##_start[] asm("_binary__"#X"_o_start"); \
++ extern char X##_end[] asm("_binary__"#X"_o_end")
++
++#define load_embedded_script(S, X) \
++ ({ \
++ scheme_load_mem ((S), X##_start, X##_end, #X); \
++ if ((S)->retcode != 0) \
++ fprintf (stderr, "Errors encountered evaluating %s\n", #X); \
++ })
++
++declare_embedded_script (init);
++declare_embedded_script (mach);
++declare_embedded_script (hurd);
++declare_embedded_script (boot);
++declare_embedded_script (bootstrap);
++declare_embedded_script (runsystem);
++
++void ffi_update (scheme *sc);
++void ffi_init (scheme *sc);
++
++pointer ffi_argz2list (scheme *sc,
++ const char *argz, size_t argz_len, const char *entry);
++void ffi_list2argz (scheme *sc, char **argz, size_t *argz_len, pointer list);
++
++// XXX
++error_t service_fsys_request (mach_port_t bootstrap,
++ mach_port_t realnode,
++ mach_msg_type_name_t realnodePoly,
++ mach_msg_timeout_t timeout,
++ mach_port_t *control);
++
++/* Forward declarations. */
++
++/* exec-startup.c */
++pointer do_handle_exec_startup (scheme *sc, pointer args);
++
++/* elf-exec.c */
++pointer do_elf_exec (scheme *sc, pointer args);
++
++/* fsys.c */
++pointer do_handle_fsys_startup (scheme *sc, pointer args);
++
++/* frob_task.c */
++pointer do_frob_task (scheme *sc, pointer args);
++
++#endif /* _HURD_BOOTSHELL_FFI_H */
+diff --git a/bootshell/frob-task.c b/bootshell/frob-task.c
+new file mode 100644
+index 0000000..05d0238
+--- /dev/null
++++ b/bootshell/frob-task.c
+@@ -0,0 +1,131 @@
++/* Supply a task (e.g. the kernel) with the command line arguments.
++
++ Copyright (C) 1993-2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <argz.h>
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <hurd.h>
++#include <mach.h>
++#include <pids.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/types.h>
++#include <unistd.h>
++
++#include "ffi.h"
++
++/* Frobnicate the given TASK and the proc server's idea of it, so the
++ command line can be read as for a normal Hurd process. */
++error_t
++frob_task (task_t kernel_task, const char *argz, size_t argz_len)
++{
++ error_t err;
++ process_t proc, kernel_proc = MACH_PORT_NULL;
++
++ int argc, i;
++ const char *entry;
++ size_t windowsz;
++ vm_address_t mine, his;
++
++ proc = getproc ();
++ if (! MACH_PORT_VALID (proc))
++ return EINVAL;
++
++ err = proc_task2proc (proc, kernel_task, &kernel_proc);
++ if (err)
++ goto out;
++
++ /* Mark the kernel task as an essential task so that we or the proc server
++ never want to task_terminate it. */
++ err = proc_mark_important (kernel_proc);
++ if (err)
++ goto out;
++
++ /* Our arguments make up the multiboot command line used to boot the
++ kernel. We'll write into the kernel task a page containing a
++ canonical argv array and argz of those words. */
++
++ argc = argz_count (argz, argz_len);
++
++ windowsz = round_page (((argc + 1) * sizeof (char *)) + argz_len);
++
++ mine = (vm_address_t) mmap (0, windowsz, PROT_READ|PROT_WRITE,
++ MAP_ANON, 0, 0);
++ if (mine == (vm_address_t) -1)
++ {
++ err = errno;
++ goto out;
++ }
++
++ err = vm_allocate (kernel_task, &his, windowsz, 1);
++ if (err)
++ {
++ error (0, err, "cannot allocate %Zu bytes in kernel task", windowsz);
++ munmap ((caddr_t) mine, windowsz);
++ goto out;
++ }
++
++ for (i = 0, entry = argz; entry != NULL;
++ ++i, entry = argz_next (argz, argz_len, entry))
++ ((char **) mine)[i] = ((char *) &((char **) his)[argc + 1]
++ + (entry - argz));
++ ((char **) mine)[argc] = NULL;
++ memcpy (&((char **) mine)[argc + 1], argz, argz_len);
++
++ /* We have the data all set up in our copy, now just write it over. */
++ err = vm_write (kernel_task, his, mine, windowsz);
++ munmap ((caddr_t) mine, windowsz);
++ if (err)
++ goto out;
++
++ /* The argument vector is set up in the kernel task at address HIS.
++ Finally, we can inform the proc server where to find it. */
++ err = proc_set_arg_locations (kernel_proc,
++ his, his + (argc * sizeof (char *)));
++ if (err)
++ error (0, err, "proc_set_arg_locations for kernel task");
++
++ out:
++ {
++ error_t e;
++ e = mach_port_deallocate (mach_task_self (), proc);
++ assert_perror (e);
++
++ if (MACH_PORT_VALID (kernel_proc))
++ e = mach_port_deallocate (mach_task_self (), kernel_proc);
++ assert_perror (e);
++ }
++ return err;
++}
++
++pointer
++do_frob_task (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("frob-task");
++ SC_ARG (sc, task_t, task, number, args);
++ SC_ARG (sc, pointer, arguments, list, args);
++ SC_ARGS_DONE (sc);
++ char *argz = NULL;
++ size_t argz_len = 0;
++ ffi_list2argz (sc, &argz, &argz_len, arguments);
++ err = frob_task (task, argz, argz_len);
++ free (argz);
++ SC_RETURN (sc);
++}
+diff --git a/bootshell/fs.c b/bootshell/fs.c
+new file mode 100644
+index 0000000..88536d8
+--- /dev/null
++++ b/bootshell/fs.c
+@@ -0,0 +1,111 @@
++/* Handles the fs protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <hurd.h>
++#include <mach.h>
++#include <mach/message.h>
++#include <pthread.h>
++#include <stdio.h>
++
++// eek #include "fs_S.h"
++
++#include "bootshell.h"
++
++mach_port_t rootnode;
++
++/* A top-level function for the paging thread that just services paging
++ requests. */
++static void *
++service_fs_requests (void *arg)
++{
++ extern boolean_t fs_server ();
++
++ int trace_demuxer (mach_msg_header_t *inp,
++ mach_msg_header_t *outp)
++ {
++ error (0, 0, "(fs-server: %d)", inp->msgh_id);
++ int i = fs_server (inp, outp);
++ return i;
++ }
++
++ // XXX specialized demuxer
++ while (1)
++ mach_msg_server (0? trace_demuxer: fs_server, 0, rootnode);
++
++ /* Not reached. */
++ return NULL;
++}
++
++error_t
++init_fs_server (void)
++{
++ error_t err;
++ pthread_t t;
++
++ err = mach_port_allocate (mach_task_self (),
++ MACH_PORT_RIGHT_RECEIVE,
++ &rootnode);
++ if (err)
++ return err;
++
++ /* Make a thread to service the fs protocol. */
++ err = pthread_create (&t, NULL, service_fs_requests, NULL);
++ if (err)
++ return err;
++ pthread_detach (t);
++
++ err = mach_port_insert_right (mach_task_self (),
++ rootnode,
++ rootnode,
++ MACH_MSG_TYPE_MAKE_SEND);
++ if (err)
++ return err;
++
++ setcrdir (rootnode); // XXX do we want this? not sure what for tbh.
++ setcwdir (rootnode);
++ portarray_template[INIT_PORT_CRDIR] = rootnode;
++ portarray_template[INIT_PORT_CWDIR] = rootnode;
++
++ return err;
++}
++
++error_t
++S_dir_lookup (file_t file,
++ char *path,
++ int flags,
++ mode_t mode,
++ enum retry_type *retry,
++ char *retryname,
++ file_t *returned_port,
++ mach_msg_type_name_t *returned_port_poly)
++{
++ if (file != rootnode)
++ return EOPNOTSUPP;
++
++ if (portarray_template[INIT_PORT_CRDIR] == rootnode)
++ /* Still no root filesystem. */
++ return EOPNOTSUPP;
++
++ *retry = FS_RETRY_NORMAL;
++ strncpy (retryname, path, sizeof (string_t));
++ *returned_port = portarray_template[INIT_PORT_CRDIR];
++ *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
++ return 0;
++}
+diff --git a/bootshell/fsys.c b/bootshell/fsys.c
+new file mode 100644
+index 0000000..5383e7f
+--- /dev/null
++++ b/bootshell/fsys.c
+@@ -0,0 +1,164 @@
++/* Handles the fsys protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <hurd.h>
++#include <mach.h>
++#include <mach/message.h>
++#include <mach/mig_support.h>
++#include <stdio.h>
++
++/* fsys client support. */
++#include <hurd.h>
++#include <hurd/fsys.h>
++
++#include "ffi.h"
++
++pointer
++do_fsys_set_options (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("fsys-set-options");
++ SC_ARG (sc, fsys_t, control, number, args);
++ char *options = NULL;
++ size_t options_len = 0;
++ SC_ARG (sc, pointer, arguments, list, args);
++ ffi_list2argz (sc, &options, &options_len, arguments);
++ SC_ARG (sc, int, do_children, number, args);
++ SC_ARGS_DONE (sc);
++ err = fsys_set_options (control, options, options_len, do_children);
++ SC_RETURN (sc);
++}
++
++pointer
++do_fsys_get_options (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("fsys_get_options");
++ SC_ARG (sc, fsys_t, control, number, args);
++ SC_ARGS_DONE (sc);
++ char *options = NULL;
++ size_t options_len = 0;
++ pointer result;
++ err = fsys_get_options (control, &options, &options_len);
++ if (err)
++ SC_RETURN (sc);
++ result = ffi_argz2list (sc, options, options_len, NULL);
++ vm_deallocate (mach_task_self (), (vm_address_t) options, options_len);
++ SC_RETURN_POINTER (sc, result);
++}
++
++/* Partial fsys server support. */
++// eek #include "fsys_S.h"
++
++#include "bootshell.h"
++#include "ffi.h"
++
++/* XXX would be nice not to use a global variable, maybe with
++ payloads. */
++static struct
++{
++ /* Filled by caller. */
++ mach_port_t bootstrap_port;
++ mach_port_t realnode;
++ mach_msg_type_name_t realnodePoly;
++
++ /* Filled by the server function. */
++ mach_port_t control_port;
++} fsys_startup_args;
++
++error_t
++S_fsys_startup (mach_port_t bootstrap,
++ int openflags,
++ mach_port_t control_port,
++ mach_port_t *realnode,
++ mach_msg_type_name_t *realnodePoly)
++{
++ assert (MACH_PORT_VALID (fsys_startup_args.bootstrap_port));
++ if (bootstrap != fsys_startup_args.bootstrap_port)
++ return EOPNOTSUPP;
++
++ fsys_startup_args.control_port = control_port;
++ *realnode = fsys_startup_args.realnode;
++ *realnodePoly = fsys_startup_args.realnodePoly;
++ return 0;
++}
++
++static boolean_t
++fsys_startup_demuxer (mach_msg_header_t *request,
++ mach_msg_header_t *reply)
++{
++ // XXX
++ extern boolean_t fsys_server (mach_msg_header_t *, mach_msg_header_t *);
++
++ switch (request->msgh_id)
++ {
++ case 22000: /* fsys_startup */
++ return fsys_server (request, reply);
++ }
++
++ /* Return MIG_BAD_ID. */
++ mig_reply_setup (request, reply);
++ return FALSE;
++}
++
++error_t
++service_fsys_request (mach_port_t bootstrap,
++ mach_port_t realnode,
++ mach_msg_type_name_t realnodePoly,
++ mach_msg_timeout_t timeout,
++ mach_port_t *control)
++{
++ error_t err;
++
++ if (! MACH_PORT_VALID (bootstrap))
++ return EINVAL;
++
++ fsys_startup_args.bootstrap_port = bootstrap;
++ fsys_startup_args.realnode = realnode;
++ fsys_startup_args.realnodePoly = realnodePoly;
++ fsys_startup_args.control_port = MACH_PORT_NULL;
++
++ err = mach_msg_server_timeout_once (fsys_startup_demuxer, 0, bootstrap,
++ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
++ timeout);
++ if (err != MACH_MSG_SUCCESS)
++ return err;
++
++ *control = fsys_startup_args.control_port;
++ return 0;
++}
++
++pointer
++do_handle_fsys_startup (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("handle-fsys-startup");
++ SC_ARG (sc, mach_port_t, bootstrap, number, args);
++ SC_ARG (sc, mach_port_t, realnode, number, args);
++ SC_ARG (sc, mach_msg_type_name_t, realnodePoly, number, args);
++ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t control = MACH_PORT_NULL;
++ err = service_fsys_request (bootstrap,
++ realnode,
++ realnodePoly,
++ timeout,
++ &control);
++ SC_RETURN_INT (sc, control);
++}
+diff --git a/bootshell/fsys.h b/bootshell/fsys.h
+new file mode 100644
+index 0000000..d6f2ea7
+--- /dev/null
++++ b/bootshell/fsys.h
+@@ -0,0 +1,28 @@
++/* Handles the fsys protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _HURD_BOOTSHELL_FSYS_H
++#define _HURD_BOOTSHELL_FSYS_H
++
++#include "ffi.h"
++
++pointer do_fsys_set_options (scheme *sc, pointer args);
++pointer do_fsys_get_options (scheme *sc, pointer args);
++
++#endif /* _HURD_BOOTSHELL_FSYS_H */
+diff --git a/bootshell/hurd.scm b/bootshell/hurd.scm
+new file mode 100644
+index 0000000..46cbfe3
+--- /dev/null
++++ b/bootshell/hurd.scm
+@@ -0,0 +1,122 @@
++;; The Hurd interface.
++;;
++;; Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++(define (touch path)
++ (letport ((p (file-name-lookup path O_CREAT #o666))))) ;; XXX mode ?
++
++(define (chown path owner) #f) ;;XXX
++
++(define (st path owner mode translator . args)
++ (if (not (null? args))
++ (for-each display args))
++ (set-passive-translator path O_CREAT mode translator)
++ (chown path owner))
++
++(define (tty n)
++ (let ((path (string-append "/dev/tty" (number->string n))))
++ (st path 'root #o600
++ `(/hurd/term
++ ,path hurdio
++ ,(string-append "/dev/vcs/" (number->string n) "/console")))))
++
++(define (have-default-memory-manager?)
++ (letport ((p (vm-set-default-memory-manager host-priv
++ MACH_PORT_NULL)))
++ (mach-port-valid? p)))
++
++
++(define (path->string x)
++ (if (string? x) x (symbol->string x)))
++
++(define (start-active-translator path args)
++ (log (path->string path) " ")
++ (bind path (start-translator (task-create mach-task-self 0) args)))
++
++(define (make-essential-devices)
++ (log "mach-defpager ")
++ (run '(/hurd/mach-defpager))
++
++ (start-active-translator '/proc '(/hurd/procfs --compatible))
++ (start-active-translator '/servers/socket/1 '(/hurd/pflocal))
++ (start-active-translator '/servers/password '(/hurd/password))
++
++ ;; We need the default pager before starting proxy-defpager.
++ (wait-for have-default-memory-manager? 1000000)
++
++ (start-active-translator '/servers/default-pager '(/hurd/proxy-defpager))
++ (start-active-translator '/dev '(/hurd/tmpfs 5M))
++ (start-active-translator '/run '(/hurd/tmpfs 15M))
++ (start-active-translator '/root '(/hurd/tmpfs 5M))
++ (start-active-translator '/tmp '(/hurd/tmpfs 15M))
++ (start-active-translator '/home '(/hurd/tmpfs 20%)) ;; XXX 20%
++
++ (st '/dev/time 'root #o644 '(/hurd/storeio --no-cache time))
++ (st '/dev/mem 'root #o660 '(/hurd/storeio --no-cache mem))
++ (st '/dev/vcs 'root #o600 '(/hurd/console))
++ (touch '/dev/console)
++
++ (map tty '(1 2 3 4 5 6))
++
++ (st '/dev/fd 'root #o666 '(/hurd/magic --directory fd))
++ (symlink 'fd/0 '/dev/stdin)
++ (symlink 'fd/1 '/dev/stdout)
++ (symlink 'fd/2 '/dev/stderr))
++
++(define (symlink target linkpath)
++ (st linkpath 'root #o644 `(/hurd/symlink ,target)))
++
++(define (reboot-hurd)
++ (letport ((startup (file-name-lookup "/servers/startup" 0 0)))
++ (startup-reboot startup host-priv RB_AUTOBOOT)))
++
++(define (halt-hurd)
++ (letport ((startup (file-name-lookup "/servers/startup" 0 0)))
++ (startup-reboot startup host-priv RB_HALT)))
++
++(define (reboot)
++ (catch (reboot-mach)
++ (reboot-hurd)))
++(define (halt)
++ (catch (halt-mach)
++ (halt-hurd)))
++
++(define ESUCCESS 0) ;
++
++;; translator linkage
++
++(define (set-passive-translator path flags mode args)
++ (letport ((node (file-name-lookup path (logior O_NOTRANS flags) mode)))
++ (file-set-translator node FS_TRANS_SET 0 0 args
++ MACH_PORT_NULL MACH_MSG_TYPE_COPY_SEND)))
++
++(define (set-active-translator path flags mode active-control)
++ (letport ((node (file-name-lookup path (logior O_NOTRANS flags) mode)))
++ (file-set-translator node 0 FS_TRANS_SET 0 '()
++ active-control MACH_MSG_TYPE_COPY_SEND)))
++(define (run argv)
++ (letport ((proc (getproc))
++ (task (task-create mach-task-self 0))
++ (child-proc (proc->task->proc proc task)))
++ (task-set-name task (car argv))
++ (_exec (file-name-lookup (car argv) O_EXEC 0)
++ task argv MACH_PORT_NULL)
++ (proc->mark-exec! child-proc)
++ (proc->mark-important! child-proc)
++ (proc->task->child! proc task)
++ (copy-send-right task)))
+diff --git a/bootshell/mach.scm b/bootshell/mach.scm
+new file mode 100644
+index 0000000..3dd98ac
+--- /dev/null
++++ b/bootshell/mach.scm
+@@ -0,0 +1,93 @@
++;; The Mach interface.
++;;
++;; Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++;; Binds a send right to an identifier, much like `let'. Deallocates
++;; the send right once it goes out of scope.
++(macro (letport form)
++ (let ((result-sym (gensym)))
++ `((lambda ((,@(caaadr form)))
++ (let ((,result-sym
++ ,(if (= 1 (length (cadr form)))
++ `(begin ,@(cddr form))
++ `(letport ,(cdadr form) ,@(cddr form)))))
++ (if (mach-port-valid? ,(caaadr form))
++ (mach-port-deallocate mach-task-self ,(caaadr form)))
++ ,result-sym)) ,@(cdaadr form))))
++
++;; TinySCHEME doesn't have define-syntax :(
++;;
++;; (define-syntax letport
++;; (syntax-rules ()
++;; ((letport ((var expr) ...) body ...)
++;; ((lambda expressions
++;; (let ((result (apply (lambda (var ...) body ...) expressions)))
++;; (map (lambda (p) (mach-port-deallocate mach-task-self p))
++;; expressions)
++;; result)) expr ...))))
++
++;; Mach port management.
++(define (mach-port-valid? p) (not (or (= p MACH_PORT_NULL)
++ (= p MACH_PORT_DEAD))))
++
++(define (make-send-right receive-right)
++ (mach-port-insert-right mach-task-self receive-right receive-right
++ MACH_MSG_TYPE_MAKE_SEND))
++(define (copy-send-right send-right)
++ (mach-port-insert-right mach-task-self send-right send-right
++ MACH_MSG_TYPE_COPY_SEND))
++
++;; Mach task interface.
++(define (task-get-kernel-port t)
++ (task-get-special-port t TASK_KERNEL_PORT))
++(define (task-get-exception-port t)
++ (task-get-special-port t TASK_EXCEPTION_PORT))
++(define (task-get-bootstrap-port t)
++ (task-get-special-port t TASK_BOOTSTRAP_PORT))
++
++(define (task-set-kernel-port t p)
++ (task-set-special-port t TASK_KERNEL_PORT p))
++(define (task-set-exception-port t p)
++ (task-set-special-port t TASK_EXCEPTION_PORT p))
++(define (task-set-bootstrap-port t p)
++ (task-set-special-port t TASK_BOOTSTRAP_PORT p))
++
++;; Mach host interface.
++(define (reboot-mach) (host-reboot host-priv RB_AUTOBOOT))
++(define (halt-mach) (host-reboot host-priv RB_HALT))
++(define (kdb-mach) (host-reboot host-priv RB_DEBUGGER))
++
++;; Default memory manager interface.
++(define (have-default-memory-manager?)
++ (letport ((p (vm-set-default-memory-manager host-priv
++ MACH_PORT_NULL)))
++ (mach-port-valid? p)))
++
++;; Tests if a device with the given NAME exists.
++(define (devprobe? name)
++ (letport ((device (catch MACH_PORT_NULL
++ (device-open device-master D_READ name))))
++ (mach-port-valid? device)))
++
++;; Insert RIGHT into TASK. Returns the name of RIGHT in TASK.
++(define (task-insert-send-right task right)
++ (let loop ((name 1))
++ (catch (loop (+ name 1))
++ (mach-port-insert-right task name right MACH_MSG_TYPE_COPY_SEND)
++ name)))
++
+diff --git a/bootshell/main.c b/bootshell/main.c
+new file mode 100644
+index 0000000..54685a7
+--- /dev/null
++++ b/bootshell/main.c
+@@ -0,0 +1,267 @@
++/* Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
++
++ Copyright (C) 1995-2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <argp.h>
++#include <assert.h>
++#include <ctype.h>
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <error.h>
++#include <hurd.h>
++#include <hurd/fshelp.h>
++#include <device/device.h>
++#include <version.h>
++
++#include "scheme.h"
++#include "scheme-private.h"
++
++#include "bootshell.h"
++#include "ffi.h"
++
++task_t bootscript_task;
++task_t rootfs_server_task;
++task_t exec_server_task;
++
++#define _HURD_RUNSYSTEM "/hurd/runsystem.scm"
++#define _BOOT_COMMAND "(boot)"
++
++char *multiboot_command_line;
++char *boot_init_program;
++boolean_t boot_pause;
++char *boot_command = _BOOT_COMMAND;
++char **global_argv;
++boolean_t booted;
++boolean_t interactive;
++
++scheme scm;
++
++/* We catch exceptions using this port. */
++mach_port_t exception_port;
++
++mach_port_t console;
++
++mach_port_t portarray_template[INIT_PORT_MAX];
++
++const char *argp_program_version = STANDARD_HURD_VERSION (bootshell);
++
++#define OPT_HOST_PRIV_PORT (-1)
++#define OPT_DEVICE_MASTER_PORT (-2)
++#define OPT_BOOTSCRIPT_TASK (-3)
++#define OPT_ROOTFS_SERVER_TASK (-4)
++#define OPT_EXEC_SERVER_TASK (-5)
++#define OPT_BOOT_CMDLINE (-6)
++#define OPT_BOOT_COMMAND (-7)
++#define OPT_BOOT_INIT_PROGRAM (-8)
++#define OPT_BOOT_PAUSE (-9)
++
++static const struct argp_option options[] =
++{
++ {"interactive", 'I', NULL, 0, "start interactive repl"},
++ {0,0,0,0, "Boot options:", -2},
++ {"multiboot-command-line", OPT_BOOT_CMDLINE, "ARGS", 0,
++ "The multiboot kernel command line"},
++ {"bootflags", 0, 0, OPTION_ALIAS|OPTION_HIDDEN},
++ {"boot-debug-pause", OPT_BOOT_PAUSE, NULL, 0,
++ "Pause for keystroke before starting bootstrap programs"},
++ {"boot-command", OPT_BOOT_COMMAND, "S-EXPRESSION", 0,
++ "Command to run, default: " _BOOT_COMMAND},
++ {"host-priv-port", OPT_HOST_PRIV_PORT, "PORT"},
++ {"device-master-port", OPT_DEVICE_MASTER_PORT, "PORT"},
++ {"bootscript-task", OPT_BOOTSCRIPT_TASK, "PORT"},
++ {"rootfs-server-task", OPT_ROOTFS_SERVER_TASK, "PORT"},
++ {"exec-server-task", OPT_EXEC_SERVER_TASK, "PORT"},
++ {0}
++};
++
++static error_t
++parse_opt (int opt, char *arg, struct argp_state *state)
++{
++ switch (opt)
++ {
++ /* Boot options */
++ case 'I':
++ interactive = 1;
++ break;
++ case OPT_DEVICE_MASTER_PORT:
++ _hurd_device_master = atoi (arg); break;
++ case OPT_HOST_PRIV_PORT:
++ _hurd_host_priv = atoi (arg); break;
++ case OPT_BOOTSCRIPT_TASK:
++ bootscript_task = atoi (arg); break;
++ case OPT_ROOTFS_SERVER_TASK:
++ rootfs_server_task = atoi (arg); break;
++ case OPT_EXEC_SERVER_TASK:
++ exec_server_task = atoi (arg); break;
++ case OPT_BOOT_CMDLINE:
++ multiboot_command_line = arg; break;
++ case OPT_BOOT_INIT_PROGRAM:
++ boot_init_program = arg; break;
++ case OPT_BOOT_PAUSE:
++ boot_pause = 1; break;
++ case OPT_BOOT_COMMAND:
++ boot_command = arg; break;
++ case ARGP_KEY_END:
++ global_argv = state->argv; break;
++ default:
++ return ARGP_ERR_UNKNOWN;
++ case ARGP_KEY_INIT:
++ case ARGP_KEY_SUCCESS:
++ case ARGP_KEY_ERROR:
++ break;
++ }
++ return 0;
++}
++
++static const char doc[] =
++ "Start and maintain hurd core servers and system run state";
++
++static const struct argp argp =
++{ options, parse_opt, 0, doc };
++
++void
++panic (const char *msg)
++{
++ puts (msg);
++ fflush (stdout);
++ _exit (127);
++}
++
++int
++main (int argc, char **argv)
++{
++ error_t err;
++ /* XXX */
++ setenv ("TERM", "mach", 1);
++ setenv ("COLS", "80", 1);
++ setenv ("LINES", "25", 1);
++
++ argp_parse (&argp, argc, argv, /*ARGP_NO_ERRS|*/ARGP_IN_ORDER, 0, 0);
++ global_argv = argv; /* For calling _hurd_new_proc_init later. */
++
++ {
++ mach_port_t proc = getproc ();
++ if (MACH_PORT_VALID (proc))
++ {
++ booted = 1;
++ err = mach_port_deallocate (mach_task_self (), proc);
++ assert_perror (err);
++ }
++ }
++
++ if (! booted)
++ {
++ err = init_exception_handling ();
++ if (err)
++ error (1, err, "init_exception_handling");
++ }
++
++ err = init_fs_server (); // XXX don't start automatically
++ if (err)
++ error (1, err, "init_fs_server");
++
++ if (MACH_PORT_VALID (_hurd_device_master))
++ {
++ err = device_open (_hurd_device_master, D_READ|D_WRITE,
++ "console", &console);
++ if (err)
++ panic ("Failed to open console.");
++
++ stdin = mach_open_devstream (console, "r");
++ stdout = stderr = mach_open_devstream (console, "w");
++ if (! stdin || ! stdout)
++ panic ("Failed to open device stream.");
++
++ setvbuf (stdout, NULL, _IONBF, 0);
++ }
++
++ if (! scheme_init (&scm))
++ error (1, errno, "scheme_init");
++
++ scheme_set_input_port_file (&scm, stdin);
++ scheme_set_output_port_file (&scm, stdout);
++
++ ffi_init (&scm);
++
++ load_embedded_script (&scm, init);
++ load_embedded_script (&scm, mach);
++ load_embedded_script (&scm, hurd);
++ load_embedded_script (&scm, boot);
++ load_embedded_script (&scm, bootstrap);
++ load_embedded_script (&scm, runsystem);
++
++ define_variable (&scm, bootscript_task);
++ define_variable (&scm, rootfs_server_task);
++ define_variable (&scm, exec_server_task);
++ define_variable (&scm, boot_pause);
++
++ define_ (&scm, "host-priv",
++ scm.vptr->mk_integer (&scm, _hurd_host_priv));
++ define_ (&scm, "device-master",
++ scm.vptr->mk_integer (&scm, _hurd_device_master));
++
++ define_variable_string (&scm, multiboot_command_line);
++ define_variable_string (&scm, boot_init_program);
++ define_variable_string (&scm, boot_command);
++ define_variable (&scm, boot_pause);
++ {
++ char *argz = NULL;
++ size_t argz_len = 0;
++ err = argz_create (argv, &argz, &argz_len);
++ assert_perror (err);
++ define_ (&scm, "argv", ffi_argz2list (&scm, argz, argz_len, NULL));
++ }
++
++ if (MACH_PORT_VALID (bootscript_task))
++ {
++ vm_size_t size;
++ vm_prot_t prot, max_prot;
++ mach_port_t obj;
++ vm_offset_t addr = 0, offs;
++ vm_inherit_t inh;
++ int shared;
++
++ err =
++ vm_region (bootscript_task, &addr, &size, &prot, &max_prot, &inh, &shared,
++ &obj, &offs);
++ if (err)
++ error (12, err, "vm_region");
++
++ vm_offset_t script;
++ size_t count;
++ err = vm_read (bootscript_task, addr, size, &script, &count);
++ if (err)
++ error (12, err, "vm_read");
++ scheme_load_mem (&scm, (char *) script, (char *) script + size, "external bootscript");
++ if (scm.retcode) {
++ fprintf (stderr, "Error: %d\n", scm.retcode);
++ }
++ }
++
++ if (! interactive)
++ scheme_load_string (&scm, boot_command);
++
++ while (1)
++ scheme_load_string (&scm, "(interactive-repl)");
++
++ /* Not reached. */
++ scheme_deinit(&scm);
++ return 0;
++}
+diff --git a/bootshell/mig-decls.h b/bootshell/mig-decls.h
+new file mode 100644
+index 0000000..76a67ff
+--- /dev/null
++++ b/bootshell/mig-decls.h
+@@ -0,0 +1,22 @@
++/* MIG declarations for bootshell.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <hurd.h>
++
++#define MIG_EOPNOTSUPP ({ abort (); EOPNOTSUPP; })
+diff --git a/bootshell/mig-mutate.h b/bootshell/mig-mutate.h
+new file mode 100644
+index 0000000..145e83a
+--- /dev/null
++++ b/bootshell/mig-mutate.h
+@@ -0,0 +1,27 @@
++/* MIG mutations for bootshell.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#define HURD_DEFAULT_PAYLOAD_TO_PORT 1
++
++#define FILE_IMPORTS \
++ import "mig-decls.h";
++#define FSYS_IMPORTS \
++ import "mig-decls.h";
++#define STARTUP_IMPORTS \
++ import "mig-decls.h";
+diff --git a/bootshell/runsystem.scm b/bootshell/runsystem.scm
+new file mode 100644
+index 0000000..86b8c16
+--- /dev/null
++++ b/bootshell/runsystem.scm
+@@ -0,0 +1,78 @@
++;; The Hurd server bootstrap.
++;;
++;; Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++(define (string-index haystack delimiter)
++ (define (index i haystack delimiter)
++ (if (= (length haystack) 0)
++ #f
++ (if (char=? (car haystack) delimiter)
++ i
++ (index (+ i 1) (cdr haystack) delimiter))))
++ (index 0 (string->list haystack) delimiter))
++
++(define (string-splitn haystack delimiter n)
++ (define (split acc haystack delimiter n)
++ (if (= (string-length haystack) 0)
++ (reverse acc)
++ (let ((i (string-index haystack delimiter)))
++ (if (not (or (eq? i #f) (= 0 n)))
++ (split (cons (substring haystack 0 i) acc)
++ (substring haystack (+ i 1) (string-length haystack))
++ delimiter (- n 1))
++ (split (cons haystack acc) "" delimiter 0)
++ ))))
++ (split '() haystack delimiter n))
++
++(define (string-split haystack delimiter)
++ (string-splitn haystack delimiter -1))
++
++(define (parse-cmdline c)
++ (define (parse args kwargs l)
++ (if (= (length l) 0)
++ (cons (reverse args) kwargs)
++ (let ((kv (string-splitn (car l) #\= 1)))
++ (if (= (length kv) 1)
++ (parse (cons (car kv) args) kwargs (cdr l))
++ (parse args (cons (cons (car kv) (cadr kv)) kwargs) (cdr l))))))
++ (parse '() '() (string-split c #\ )))
++
++(define (boot)
++ (let* ((arguments (parse-cmdline multiboot-command-line))
++ (:flag (lambda (key) (member key (car arguments))))
++ (:kwarg (lambda (key default)
++ (let ((value (assoc key (cdr arguments))))
++ (if (equal? value #f)
++ (default)
++ (cdr value)))))
++ (init (:kwarg "init" (lambda () "/sbin/init"))))
++
++ (bootstrap (list
++ (lambda () (first-stage (:kwarg "root" (lambda () "xxx"))))
++ early-startup
++ second-stage
++ start-terminal
++ startup-standalone
++ pflocal
++ mach-defpager
++ rootfs-update
++ ;(lambda () (boot! init))
++ (lambda ()
++ (run '(/sbin/console-run --console=/dev/console -- /bin/bash))
++ (sleep 60))
++ ))))
+diff --git a/bootshell/scheme-config.h b/bootshell/scheme-config.h
+new file mode 100644
+index 0000000..127df9f
+--- /dev/null
++++ b/bootshell/scheme-config.h
+@@ -0,0 +1,31 @@
++/* TinyScheme configuration.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#define STANDALONE 0
++#define USE_MATH 0
++#define USE_CHAR_CLASSIFIERS 1
++#define USE_ASCII_NAMES 1
++#define USE_STRING_PORTS 1
++#define USE_ERROR_HOOK 1
++#define USE_TRACING 1
++#define USE_COLON_HOOK 0
++#define USE_DL 0
++#define USE_PLIST 0
++#define USE_INTERFACE 1
++#define SHOW_ERROR_LINE 1
+diff --git a/bootshell/startup.c b/bootshell/startup.c
+new file mode 100644
+index 0000000..4b9f71d
+--- /dev/null
++++ b/bootshell/startup.c
+@@ -0,0 +1,508 @@
++/* Handles the startup protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <hurd.h>
++#include <hurd/startup.h>
++#include <mach.h>
++#include <mach/message.h>
++#include <mach/mig_support.h>
++#include <mach/notify.h>
++#include <pthread.h>
++#include <stdio.h>
++#include <string.h>
++
++#include "startup_reply_U.h"
++// eek #include "startup_S.h"
++// eek #include "fsys_S.h"
++
++extern boolean_t startup_server (mach_msg_header_t *, mach_msg_header_t *);
++extern boolean_t fsys_server (mach_msg_header_t *, mach_msg_header_t *);
++
++#include "bootshell.h"
++#include "ffi.h"
++
++/* Handling of `startup_essential_task'. */
++
++static mach_port_t early_startup_port;
++
++/* This structure keeps track of each registered essential task. */
++struct port_string_tuple
++ {
++ struct port_string_tuple *next;
++ task_t port;
++ char *name;
++ };
++
++static struct port_string_tuple *essential_tasks;
++static struct port_string_tuple *registered_tasks;
++
++/* Record an essential task in the list. */
++static error_t
++add_tuple (struct port_string_tuple **list, mach_port_t port, const char *name)
++{
++ struct port_string_tuple *et;
++
++ et = malloc (sizeof *et);
++ if (et == NULL)
++ goto out;
++
++ et->port = port;
++ et->name = strdup (name);
++ if (et->name == NULL)
++ goto out;
++
++ et->next = *list;
++ *list = et;
++ return 0;
++
++ out:
++ free (et);
++ return ENOMEM;
++}
++
++/* fsys_goaway for early-boot /servers/startup. */
++error_t
++S_fsys_goaway (mach_port_t fsys,
++ int flags)
++{
++ if (fsys != early_startup_port)
++ return EOPNOTSUPP;
++ // XXX keep going = 0
++ return 0;
++}
++
++/* fsys_getroot for early-boot /servers/startup. */
++error_t
++S_fsys_getroot (mach_port_t fsys,
++ mach_port_t dotdotnode,
++ uid_t *uids, size_t nuids,
++ uid_t *gids, size_t ngids,
++ int flags,
++ retry_type *do_retry,
++ char *retry_name,
++ mach_port_t *ret,
++ mach_msg_type_name_t *rettype)
++{
++ if (fsys != early_startup_port)
++ return EOPNOTSUPP;
++
++ *do_retry = FS_RETRY_NORMAL;
++ *retry_name = '\0';
++ *ret = early_startup_port;
++ *rettype = MACH_MSG_TYPE_MAKE_SEND;
++ return 0;
++}
++
++error_t
++S_startup_essential_task (startup_t server,
++ mach_port_t reply_port,
++ mach_msg_type_name_t reply_portPoly,
++ mach_port_t task,
++ mach_port_t excpt,
++ string_t name,
++ mach_port_t credential)
++{
++ error_t err;
++ if (server != early_startup_port)
++ return EOPNOTSUPP;
++ if (credential != _hurd_host_priv)
++ return EPERM;
++
++ err = mach_port_deallocate (mach_task_self (), credential);
++ assert_perror (err);
++
++ if (MACH_PORT_VALID (excpt))
++ {
++ error (0, 0,
++ "Oh dear, someone actually send us their exception port.\n"
++ "I'm going to destroy it. Please investigate.");
++ err = mach_port_destroy (mach_task_self (), excpt);
++ assert_perror (err);
++ }
++
++ err = add_tuple (&essential_tasks, task, name);
++ if (err)
++ return err;
++
++ return 0;
++}
++
++kern_return_t
++S_startup_request_notification (mach_port_t server,
++ mach_port_t notify,
++ char *name)
++{
++ if (server != early_startup_port)
++ return EOPNOTSUPP;
++
++ return add_tuple (&registered_tasks, notify, name);
++}
++
++static boolean_t
++early_startup_demuxer (mach_msg_header_t *request,
++ mach_msg_header_t *reply)
++{
++ /* XXX hardcoded msgh_ids */
++ switch (request->msgh_id)
++ {
++ case 29000: /* startup_essential_task */
++ case 29001: /* startup_request_notification */
++ return startup_server (request, reply);
++ case 22001: /* fsys_goaway */
++ case 22002: /* fsys_getroot */
++ return fsys_server (request, reply);
++ }
++
++ /* Return MIG_BAD_ID. */
++ mig_reply_setup (request, reply);
++ return FALSE;
++}
++
++static void *
++service_early_startup_requests (void *arg)
++{
++ // XXX while (keep_going) ...
++ while (1)
++ mach_msg_server (early_startup_demuxer, 0,
++ early_startup_port);
++
++ /* Not reached. */
++ return NULL;
++}
++
++static error_t
++start_handling_early_startup (mach_port_t startup_port)
++{
++ error_t err;
++ pthread_t t;
++
++ if (MACH_PORT_VALID (early_startup_port))
++ return EINVAL;
++ early_startup_port = startup_port;
++
++ /* Make a thread to service `startup_essential_task' requests. */
++ err = pthread_create (&t, NULL, service_early_startup_requests,
++ NULL);
++ if (err)
++ return err;
++ pthread_detach (t);
++
++ return err;
++}
++
++pointer
++do_start_handling_early_startup (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("start-handling-early-startup");
++ SC_ARG (sc, mach_port_t, server_port, number, args);
++ SC_ARGS_DONE (sc);
++ err = start_handling_early_startup (server_port);
++ SC_RETURN (sc);
++}
++
++pointer
++do_get_essential_tasks (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("get-essential-tasks");
++ SC_ARGS_DONE (sc);
++ pointer result = sc->NIL;
++ struct port_string_tuple *et;
++ for (et = essential_tasks; et; et = et->next)
++#define IMC(A, B) _cons (sc, (A), (B), 1)
++ result = IMC (IMC (mk_integer (sc, et->port),
++ mk_string (sc, et->name)),
++ result);
++#undef IMC
++ SC_RETURN_POINTER (sc, result);
++}
++
++pointer
++do_get_registered_tasks (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("get-registered-tasks");
++ SC_ARGS_DONE (sc);
++ pointer result = sc->NIL;
++ struct port_string_tuple *rt;
++ for (rt = registered_tasks; rt; rt = rt->next)
++#define IMC(A, B) _cons (sc, (A), (B), 1)
++ result = IMC (IMC (mk_integer (sc, rt->port),
++ mk_string (sc, rt->name)),
++ result);
++#undef IMC
++ SC_RETURN_POINTER (sc, result);
++}
++
++/* Client stubs for startup. */
++pointer
++do_startup_essential_task (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("startup-essential-task");
++ SC_ARG (sc, mach_port_t, startup, number, args);
++ SC_ARG (sc, mach_port_t, task, number, args);
++ SC_ARG (sc, mach_port_t, exception, number, args);
++ SC_ARG (sc, char *, name, string, args);
++ SC_ARG (sc, mach_port_t, credential, number, args);
++ SC_ARGS_DONE (sc);
++ err = startup_essential_task (startup, task, exception, name, credential);
++ SC_RETURN (sc);
++}
++
++pointer
++do_startup_request_notification (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("startup-request-notification");
++ SC_ARG (sc, mach_port_t, startup, number, args);
++ SC_ARG (sc, mach_port_t, notify_port, number, args);
++ SC_ARG (sc, char *, name, string, args);
++ SC_ARGS_DONE (sc);
++ err = startup_request_notification (startup,
++ notify_port, MACH_MSG_TYPE_COPY_SEND,
++ name);
++ SC_RETURN (sc);
++}
++
++pointer
++do_startup_reboot (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("startup-reboot");
++ SC_ARG (sc, mach_port_t, startup, number, args);
++ SC_ARG (sc, mach_port_t, credential, number, args);
++ SC_ARG (sc, int, flags, number, args);
++ SC_ARGS_DONE (sc);
++ err = startup_reboot (startup, credential, flags);
++ SC_RETURN (sc);
++}
++
++/* Handling of `startup_procinit'. */
++
++/* XXX would be nice not to use a global variable, maybe with
++ payloads. */
++static struct
++{
++ /* Filled by caller. */
++ mach_port_t bootstrap_port;
++
++ /* Filled by the server function. */
++ mach_port_t reply;
++ mach_msg_type_name_t replyPoly;
++ process_t procserver;
++} startup_procinit_args;
++
++kern_return_t
++S_startup_procinit (startup_t bootstrap,
++ mach_port_t reply,
++ mach_msg_type_name_t replyPoly,
++ process_t procserver,
++ mach_port_t *startuptask,
++ auth_t *auth,
++ mach_port_t *hostpriv,
++ mach_msg_type_name_t *hostprivPoly,
++ mach_port_t *devmaster,
++ mach_msg_type_name_t *devmasterPoly)
++{
++ if (bootstrap != startup_procinit_args.bootstrap_port)
++ return EOPNOTSUPP;
++
++ startup_procinit_args.reply = reply;
++ startup_procinit_args.replyPoly = replyPoly;
++ startup_procinit_args.procserver = procserver;
++ return MIG_NO_REPLY;
++}
++
++boolean_t
++startup_procinit_demuxer (mach_msg_header_t *request,
++ mach_msg_header_t *reply)
++{
++ if (request->msgh_id != 29003) /* XXX hardcoded msgh_id */
++ {
++ /* Return MIG_BAD_ID. */
++ mig_reply_setup (request, reply);
++ return FALSE;
++ }
++ return startup_server (request, reply);
++}
++
++error_t
++service_startup_procinit_request (mach_port_t bootstrap,
++ mach_msg_timeout_t timeout,
++ mach_port_t *reply,
++ mach_msg_type_name_t *replyPoly,
++ process_t *procserver)
++{
++ error_t err;
++
++ if (! MACH_PORT_VALID (bootstrap))
++ return EINVAL;
++
++ startup_procinit_args.bootstrap_port = bootstrap;
++
++ err = mach_msg_server_timeout_once (startup_procinit_demuxer, 0, bootstrap,
++ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
++ timeout);
++ if (err != MACH_MSG_SUCCESS)
++ return err;
++
++ *reply = startup_procinit_args.reply;
++ *replyPoly = startup_procinit_args.replyPoly;
++ *procserver = startup_procinit_args.procserver;
++ return 0;
++}
++
++pointer
++do_handle_startup_procinit (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("handle-startup-procinit");
++ SC_ARG (sc, mach_port_t, bootstrap, number, args);
++ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t reply;
++ mach_msg_type_name_t replyPoly;
++ mach_port_t proc;
++ err = service_startup_procinit_request (bootstrap, timeout,
++ &reply, &replyPoly, &proc);
++#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
++ SC_RETURN_POINTER (sc, IMC (reply, IMC (replyPoly, IMC (proc, sc->NIL))));
++#undef IMC
++}
++
++pointer
++do_startup_procinit_reply (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("startup-procinit-reply");
++ SC_ARG (sc, mach_port_t, reply, number, args);
++ SC_ARG (sc, mach_msg_type_name_t, replyPoly, number, args);
++ SC_ARG (sc, int, retCode, number, args);
++ SC_ARG (sc, mach_port_t, startup_task, number, args);
++ SC_ARG (sc, mach_port_t, authserver, number, args);
++ SC_ARG (sc, mach_port_t, host_priv, number, args);
++ SC_ARG (sc, mach_port_t, device_master, number, args);
++ SC_ARGS_DONE (sc);
++ err = startup_procinit_reply (reply, replyPoly, retCode,
++ startup_task, authserver,
++ host_priv, MACH_MSG_TYPE_COPY_SEND,
++ device_master, MACH_MSG_TYPE_COPY_SEND);
++ SC_RETURN (sc);
++}
++
++/* Handling of `startup_authinit'. */
++
++/* XXX would be nice not to use a global variable, maybe with
++ payloads. */
++static struct
++{
++ /* Filled by caller. */
++ mach_port_t bootstrap_port;
++
++ /* Filled by the server function. */
++ mach_port_t reply;
++ mach_msg_type_name_t replyPoly;
++ mach_port_t authserver;
++} startup_authinit_args;
++
++/* Called by the auth server when it starts up. */
++
++kern_return_t
++S_startup_authinit (startup_t bootstrap,
++ mach_port_t reply,
++ mach_msg_type_name_t replyPoly,
++ mach_port_t auth,
++ mach_port_t *proc,
++ mach_msg_type_name_t *procPoly)
++{
++ if (bootstrap != startup_authinit_args.bootstrap_port)
++ return EOPNOTSUPP;
++
++ startup_authinit_args.reply = reply;
++ startup_authinit_args.replyPoly = replyPoly;
++ startup_authinit_args.authserver = auth;
++ return MIG_NO_REPLY;
++}
++
++boolean_t
++startup_authinit_demuxer (mach_msg_header_t *request,
++ mach_msg_header_t *reply)
++{
++ if (request->msgh_id != 29004) /* XXX hardcoded msgh_id */
++ {
++ /* Return MIG_BAD_ID. */
++ mig_reply_setup (request, reply);
++ return FALSE;
++ }
++ return startup_server (request, reply);
++}
++
++error_t
++service_startup_authinit_request (mach_port_t bootstrap,
++ mach_msg_timeout_t timeout,
++ mach_port_t *reply,
++ mach_msg_type_name_t *replyPoly,
++ mach_port_t *authserver)
++{
++ error_t err;
++
++ if (! MACH_PORT_VALID (bootstrap))
++ return EINVAL;
++
++ startup_authinit_args.bootstrap_port = bootstrap;
++
++ err = mach_msg_server_timeout_once (startup_authinit_demuxer, 0, bootstrap,
++ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
++ timeout);
++ if (err != MACH_MSG_SUCCESS)
++ return err;
++
++ *reply = startup_authinit_args.reply;
++ *replyPoly = startup_authinit_args.replyPoly;
++ *authserver = startup_authinit_args.authserver;
++ return 0;
++}
++
++pointer
++do_handle_startup_authinit (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("handle-startup-authinit");
++ SC_ARG (sc, mach_port_t, bootstrap, number, args);
++ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
++ SC_ARGS_DONE (sc);
++ mach_port_t reply;
++ mach_msg_type_name_t replyPoly;
++ mach_port_t auth;
++ err = service_startup_authinit_request (bootstrap, timeout,
++ &reply, &replyPoly, &auth);
++#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
++ SC_RETURN_POINTER (sc, IMC (reply, IMC (replyPoly, IMC (auth, sc->NIL))));
++#undef IMC
++}
++
++pointer
++do_startup_authinit_reply (scheme *sc, pointer args)
++{
++ SC_FFI_PROLOG ("startup-authinit-reply");
++ SC_ARG (sc, mach_port_t, reply, number, args);
++ SC_ARG (sc, mach_msg_type_name_t, replyPoly, number, args);
++ SC_ARG (sc, int, retCode, number, args);
++ SC_ARG (sc, mach_port_t, authproc, number, args);
++ SC_ARGS_DONE (sc);
++ err = startup_authinit_reply (reply, replyPoly, retCode, authproc,
++ MACH_MSG_TYPE_COPY_SEND);
++ SC_RETURN (sc);
++}
+diff --git a/bootshell/startup.h b/bootshell/startup.h
+new file mode 100644
+index 0000000..0fa9b4c
+--- /dev/null
++++ b/bootshell/startup.h
+@@ -0,0 +1,36 @@
++/* Handles the startup protocol.
++
++ Copyright (C) 2015 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. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _HURD_BOOTSHELL_STARTUP_H
++#define _HURD_BOOTSHELL_STARTUP_H
++
++#include <ffi.h>
++
++pointer do_handle_startup_procinit (scheme *sc, pointer args);
++pointer do_handle_startup_authinit (scheme *sc, pointer args);
++pointer do_startup_procinit_reply (scheme *sc, pointer args);
++pointer do_startup_authinit_reply (scheme *sc, pointer args);
++pointer do_start_handling_early_startup (scheme *sc, pointer args);
++pointer do_get_essential_tasks (scheme *sc, pointer args);
++pointer do_get_registered_tasks (scheme *sc, pointer args);
++pointer do_startup_essential_task (scheme *sc, pointer args);
++pointer do_startup_request_notification (scheme *sc, pointer args);
++pointer do_startup_reboot (scheme *sc, pointer args);
++
++#endif /* _HURD_BOOTSHELL_STARTUP_H */
+diff --git a/bootshell/utils.c b/bootshell/utils.c
+new file mode 100644
+index 0000000..9fcf17c
+--- /dev/null
++++ b/bootshell/utils.c
+@@ -0,0 +1,118 @@
++#include <assert.h>
++#include <hurd.h>
++#include <mach.h>
++#include <mach/message.h>
++#include <stdarg.h>
++#include <stdio.h>
++
++mach_msg_return_t
++mach_msg_server_timeout_once (boolean_t (*demux) (mach_msg_header_t *request,
++ mach_msg_header_t *reply),
++ mach_msg_size_t max_size,
++ mach_port_t rcv_name,
++ mach_msg_option_t option,
++ mach_msg_timeout_t timeout)
++{
++ mig_reply_header_t *request, *reply;
++ mach_msg_return_t mr;
++
++ if (! MACH_PORT_VALID (rcv_name))
++ return EINVAL;
++
++ if (max_size == 0)
++ {
++#ifdef MACH_RCV_LARGE
++ option |= MACH_RCV_LARGE;
++ max_size = 2 * __vm_page_size; /* Generic. Good? XXX */
++#else
++ max_size = 4 * __vm_page_size; /* XXX */
++#endif
++ }
++
++ request = alloca (max_size);
++ reply = alloca (max_size);
++
++ mr = mach_msg (&request->Head, MACH_RCV_MSG|option,
++ 0, max_size, rcv_name,
++ timeout, MACH_PORT_NULL);
++ if (mr != MACH_MSG_SUCCESS)
++ return mr;
++
++ /* We have a request message. Pass it to DEMUX for processing. */
++ (void) (*demux) (&request->Head, &reply->Head);
++ assert (reply->Head.msgh_size <= max_size);
++
++ switch (reply->RetCode)
++ {
++ case KERN_SUCCESS:
++ /* Hunky dory. */
++ break;
++
++ case MIG_NO_REPLY:
++ /* The server function wanted no reply sent.
++ Loop for another request. */
++ return 0;
++
++ default:
++ /* Some error; destroy the request message to release any
++ port rights or VM it holds. Don't destroy the reply port
++ right, so we can send an error message. */
++ request->Head.msgh_remote_port = MACH_PORT_NULL;
++ mach_msg_destroy (&request->Head);
++ break;
++ }
++
++ if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
++ {
++ /* No reply port, so destroy the reply. */
++ if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
++ mach_msg_destroy (&reply->Head);
++ return reply->RetCode;
++ }
++
++ /* Send the reply. */
++ mr = mach_msg (&reply->Head,
++ MACH_SEND_MSG|option,
++ reply->Head.msgh_size, max_size, rcv_name,
++ timeout, MACH_PORT_NULL);
++
++ /* See if a message error occurred. */
++ if (mr == MACH_SEND_INVALID_DEST)
++ /* The reply can't be delivered, so destroy it. This error
++ indicates only that the requester went away, so we
++ continue and get the next request. */
++ mach_msg_destroy (&reply->Head);
++
++ return mr != 0 ? mr : reply->RetCode;
++}
++
++/* Fill in default response. */
++void
++mig_reply_setup (
++ const mach_msg_header_t *in,
++ mach_msg_header_t *out)
++{
++ static const mach_msg_type_t RetCodeType = {
++ /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
++ /* msgt_size = */ 32,
++ /* msgt_number = */ 1,
++ /* msgt_inline = */ TRUE,
++ /* msgt_longform = */ FALSE,
++ /* msgt_deallocate = */ FALSE,
++ /* msgt_unused = */ 0
++ };
++
++#define InP (in)
++#define OutP ((mig_reply_header_t *) out)
++ OutP->Head.msgh_bits =
++ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InP->msgh_bits), 0);
++ OutP->Head.msgh_size = sizeof *OutP;
++ OutP->Head.msgh_remote_port = InP->msgh_remote_port;
++ OutP->Head.msgh_local_port = MACH_PORT_NULL;
++ OutP->Head.msgh_seqno = 0;
++ OutP->Head.msgh_id = InP->msgh_id + 100;
++ OutP->RetCodeType = RetCodeType;
++ OutP->RetCode = MIG_BAD_ID;
++#undef InP
++#undef OutP
++}
+diff --git a/config.make.in b/config.make.in
+index 0f1390a..012bcd5 100644
+--- a/config.make.in
++++ b/config.make.in
+@@ -95,6 +95,10 @@ HAVE_BLKID = @HAVE_BLKID@
+ libblkid_CFLAGS = @libblkid_CFLAGS@
+ libblkid_LIBS = @libblkid_LIBS@
+
++# How to compile and link against libreadline.
++HAVE_READLINE = @HAVE_READLINE@
++libreadline_LIBS = @libreadline_LIBS@
++
+ # Whether Sun RPC support is available.
+ HAVE_SUN_RPC = @HAVE_SUN_RPC@
+
+diff --git a/configure.ac b/configure.ac
+index 124eb07..819b264 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -341,6 +341,21 @@ PKG_CHECK_MODULES([libblkid], [blkid],
+ AC_SUBST([libblkid_LIBS])
+ AC_SUBST([libblkid_CFLAGS])
+
++AC_ARG_WITH([readline],
++ [AS_HELP_STRING([--without-readline], [disable support for readline])],
++ [],
++ [with_readline=yes])
++
++LIBREADLINE=
++AS_IF([test "x$with_readline" != xno],
++ [AC_CHECK_LIB([readline], [main],
++ [AC_SUBST([libreadline_LIBS], ["-lreadline -lhistory -lncurses"])
++ AC_DEFINE([HAVE_LIBREADLINE], [1], [Define if you have libreadline])
++ ],
++ [AC_MSG_FAILURE(
++ [readline test failed (--without-readline to disable)])],
++ [-lncurses])])
++
+ AC_CONFIG_FILES([config.make ${makefiles}])
+ AC_OUTPUT
+
+--
+2.1.4
+