diff options
Diffstat (limited to 'debian/patches/bootshell0007-XXX-bootshell.patch')
-rw-r--r-- | debian/patches/bootshell0007-XXX-bootshell.patch | 4500 |
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) ®s, ®_size); ++ regs.eip = (int) startpc; ++ regs.uesp = (int) arg_pos; ++ err = thread_set_state (thread, i386_THREAD_STATE, ++ (thread_state_t) ®s, 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 (®istered_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 + |