From 252043ce8701a48400929319552da5a8f31288d7 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/10] XXX bootshell XXX hack in toplevel Makefile. --- Makefile | 1 + bootshell/Makefile | 69 +++ bootshell/boot.scm | 278 +++++++++++ bootshell/bootshell.h | 49 ++ bootshell/elf-exec.c | 200 ++++++++ bootshell/exceptions.c | 92 ++++ bootshell/exec-startup.c | 182 +++++++ bootshell/ffi.c | 1208 +++++++++++++++++++++++++++++++++++++++++++++ bootshell/ffi.h | 167 +++++++ bootshell/frob-task.c | 131 +++++ bootshell/fs.c | 111 +++++ bootshell/fsys.c | 123 +++++ bootshell/main.c | 264 ++++++++++ bootshell/mig-decls.h | 22 + bootshell/mig-mutate.h | 27 + bootshell/runsystem.scm | 211 ++++++++ bootshell/scheme-config.h | 31 ++ bootshell/startup.c | 508 +++++++++++++++++++ bootshell/startup.h | 36 ++ bootshell/utils.c | 118 +++++ config.make.in | 4 + configure.ac | 15 + 22 files changed, 3847 insertions(+) create mode 100644 bootshell/Makefile create mode 100644 bootshell/boot.scm create mode 100644 bootshell/bootshell.h 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/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 3178740..3a2c2ed 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..ea9bc60 --- /dev/null +++ b/bootshell/Makefile @@ -0,0 +1,69 @@ +# 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 \ + boot.scm \ + runsystem.scm \ + +SRCS := \ + scheme.c \ + main.c \ + exceptions.c \ + fs.c \ + fsys.c \ + exec-startup.c \ + startup.c \ + utils.c \ + ffi.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..1cd1723 --- /dev/null +++ b/bootshell/boot.scm @@ -0,0 +1,278 @@ +;; 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 . */ + +;; 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)) + +;; 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)) + +;; 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 ...)))) + +;; task management + +(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)) + +;; Hurd server bootstrap. + +(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))) + +;; 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. +")) + +(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-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)) + +(define (reboot) + (catch (reboot-mach) + (reboot-hurd))) +(define (halt) + (catch (halt-mach) + (halt-hurd))) + +;; 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")) + +;; XXX + +(define log display) + +;; 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 . */ + +#ifndef _HURD_BOOTSHELL_H +#define _HURD_BOOTSHELL_H + +#include + +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/elf-exec.c b/bootshell/elf-exec.c new file mode 100644 index 0000000..c8a8e14 --- /dev/null +++ b/bootshell/elf-exec.c @@ -0,0 +1,200 @@ +/* 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 . */ + +#include +#include +#include +#include +#include +#include +#include /* For VM_XXX_ADDRESS */ +#include +#include +#include +#include +#include + +#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; + vm_allocate (task, &stack_start, stack_end - stack_start, FALSE); + 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)); + vm_write (task, trunc_page ((vm_offset_t) arg_pos), (vm_address_t) args, + stack_end - trunc_page ((vm_offset_t) arg_pos)); + munmap ((caddr_t) args, + stack_end - trunc_page ((vm_offset_t) arg_pos)); + + thread_create (task, &thread); + +#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; + thread_set_state (thread, i386_THREAD_STATE, + (thread_state_t) ®s, reg_size); + } +#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); + 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 . */ + +#include +#include +#include + +#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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +// 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..d3751d9 --- /dev/null +++ b/bootshell/ffi.c @@ -0,0 +1,1208 @@ +/* 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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_LIBREADLINE +#include +#include +#endif + +#include "bootshell.h" +#include "ffi.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); + SC_RETURN_STRING (sc, get_current_dir_name ()); +} + +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) +{ + while (sc->vptr->is_pair (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); + list = sc->vptr->pair_cdr (list); + } +} + +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 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..a563454 --- /dev/null +++ b/bootshell/ffi.h @@ -0,0 +1,167 @@ +/* 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 . */ + +#ifndef _HURD_BOOTSHELL_FFI_H +#define _HURD_BOOTSHELL_FFI_H + +#include +#include + +#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 *); + +#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); \ + if ((S)->retcode != 0) \ + fprintf (stderr, "Errors encountered evaluating %s\n", #X); \ + }) + +declare_embedded_script (init); +declare_embedded_script (boot); +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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . */ + +#include +#include +#include +#include +#include +#include + +// 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..a92b90d --- /dev/null +++ b/bootshell/fsys.c @@ -0,0 +1,123 @@ +/* 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 . */ + +#include +#include +#include +#include +#include +#include + +// 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; + err = service_fsys_request (bootstrap, + realnode, + realnodePoly, + timeout, + &control); + SC_RETURN_INT (sc, control); +} diff --git a/bootshell/main.c b/bootshell/main.c new file mode 100644 index 0000000..958b40b --- /dev/null +++ b/bootshell/main.c @@ -0,0 +1,264 @@ +/* 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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, boot); + 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); + 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 . */ + +#include + +#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 . */ + +#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..befb07c --- /dev/null +++ b/bootshell/runsystem.scm @@ -0,0 +1,211 @@ +;; 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 . */ + +(define timeout 1000) ; 1 second + +(define (pause) + (if (= 1 boot-pause) (prompt "Press enter to continue..."))) + +;; 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))) + +;; 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)))) + +;; 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)) + +;; 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 (bootstrap rootfs-args exec-args) + (log "Hurd server bootstrap: ") + + (log "rootfs ") + (let ((rootfs-control (bind-root (resume-translator rootfs-server-task + rootfs-args))) + (startup-control (mach-port-allocate mach-task-self + MACH_PORT_RIGHT_RECEIVE)) + (proc-task (task-create mach-task-self 0)) + (auth-task (task-create mach-task-self 0)) + ;; Projections for the cookies returned by bootstrap-*. + (:reply car) (:replyPoly cadr) (:server caddr)) + (start-handling-early-startup startup-control) + (set-active-translator "/servers/startup" 0 0 + (make-send-right startup-control)) + (log "exec ") + (bind "/servers/exec" (resume-translator exec-server-task exec-args)) + + ;; 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"))))) + (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)) + + (mach-port-deallocate mach-task-self (:server pc)) + (mach-port-deallocate mach-task-self (:server ac)))) + + (log "console ") + (bind "/dev/console" + (start-translator (task-create mach-task-self 0) + '("/hurd/term" "/dev/console" "device" "console"))) + + (letport ((term (file-name-lookup "/dev/console" O_RDWR 0))) + (bind-term term)) + + ;; If we made it this far, we can use libreadline! + (enable-readline) + + ;; The standalone startup server watches essential servers, and + ;; handles the system shutdown. + (log "startup ") + (bind "/servers/startup" + (start-translator (task-create mach-task-self 0) + '("/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)))) + + (log "pflocal ") + (bind "/servers/socket/1" + (start-translator (task-create mach-task-self 0) + '("/hurd/pflocal"))) + + (log "done.\n")) + +(define (boot) + (catch (panic "Hurd bootstrap failed: " (car last-exception) "\n") + (bootstrap '() '())) + + (shell (lambda (prefix) + (prompt-append-prefix + (string-append "runsystem@" (hostname) " " (getcwd) " ") prefix)))) 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 . */ + +#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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . */ + +#ifndef _HURD_BOOTSHELL_STARTUP_H +#define _HURD_BOOTSHELL_STARTUP_H + +#include + +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 +#include +#include +#include +#include +#include + +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 71f3a0e..898564e 100644 --- a/configure.ac +++ b/configure.ac @@ -333,6 +333,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