diff options
Diffstat (limited to 'boot/userland-boot.c')
-rw-r--r-- | boot/userland-boot.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/boot/userland-boot.c b/boot/userland-boot.c index 7b856147..583078f1 100644 --- a/boot/userland-boot.c +++ b/boot/userland-boot.c @@ -17,11 +17,19 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <a.out.h> +#include <elf.h> +#include <fcntl.h> #include <mach.h> +#include <mach/machine/vm_param.h> /* For VM_XXX_ADDRESS */ #include <stdlib.h> #include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> #include <errno.h> #include <error.h> + #include "boot_script.h" void * @@ -115,3 +123,206 @@ boot_script_insert_task_port (struct cmd *cmd, task_t task, mach_port_t *name) { return boot_script_insert_right (cmd, task, name); } + +char *useropen_dir; + +static int +useropen (const char *name, int flags, int mode) +{ + if (useropen_dir) + { + static int dlen; + if (!dlen) dlen = strlen (useropen_dir); + { + int len = strlen (name); + char try[dlen + 1 + len + 1]; + int fd; + memcpy (try, useropen_dir, dlen); + try[dlen] = '/'; + memcpy (&try[dlen + 1], name, len + 1); + fd = open (try, flags, mode); + if (fd >= 0) + return fd; + } + } + return open (name, flags, mode); +} + +static vm_address_t +load_image (task_t t, + char *file) +{ + int fd; + union + { + struct exec a; + Elf32_Ehdr e; + } hdr; + char msg[] = ": cannot open bootstrap file\n"; + + fd = useropen (file, O_RDONLY, 0); + + if (fd == -1) + { + write (2, file, strlen (file)); + write (2, msg, sizeof msg - 1); + task_terminate (t); + exit (1); + } + + read (fd, &hdr, sizeof hdr); + /* File must have magic ELF number. */ + if (hdr.e.e_ident[0] == 0177 && hdr.e.e_ident[1] == 'E' && + hdr.e.e_ident[2] == 'L' && hdr.e.e_ident[3] == 'F') + { + Elf32_Phdr phdrs[hdr.e.e_phnum], *ph; + lseek (fd, hdr.e.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; + + vm_allocate (t, (vm_address_t*)&ph->p_vaddr, ph->p_memsz, 0); + vm_write (t, ph->p_vaddr, buf, bufsz); + munmap ((caddr_t) buf, bufsz); + 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)); + } + return hdr.e.e_entry; + } + else + { + /* a.out */ + int magic = N_MAGIC (hdr.a); + int headercruft; + vm_address_t base = 0x10000; + int rndamount, amount; + vm_address_t bsspagestart, bssstart; + char *buf; + + headercruft = sizeof (struct exec) * (magic == ZMAGIC); + + amount = headercruft + hdr.a.a_text + hdr.a.a_data; + rndamount = round_page (amount); + buf = mmap (0, rndamount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + lseek (fd, sizeof hdr.a - headercruft, SEEK_SET); + read (fd, buf, amount); + vm_allocate (t, &base, rndamount, 0); + vm_write (t, base, (vm_address_t) buf, rndamount); + if (magic != OMAGIC) + vm_protect (t, base, trunc_page (headercruft + hdr.a.a_text), + 0, VM_PROT_READ | VM_PROT_EXECUTE); + munmap ((caddr_t) buf, rndamount); + + bssstart = base + hdr.a.a_text + hdr.a.a_data + headercruft; + bsspagestart = round_page (bssstart); + vm_allocate (t, &bsspagestart, + hdr.a.a_bss - (bsspagestart - bssstart), 0); + + return hdr.a.a_entry; + } +} + +int +boot_script_exec_cmd (void *hook, + mach_port_t task, char *path, int argc, + char **argv, char *strings, int stringlen) +{ + 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; + + write (2, path, strlen (path)); + for (i = 1; i < argc; ++i) + { + int quote = !! index (argv[i], ' ') || !! index (argv[i], '\t'); + write (2, " ", 1); + if (quote) + write (2, "\"", 1); + write (2, argv[i], strlen (argv[i])); + if (quote) + write (2, "\"", 1); + } + write (2, "\r\n", 2); + + startpc = load_image (task, path); + 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); + } +#elif defined(ALPHA_THREAD_STATE_COUNT) + { + struct alpha_thread_state regs; + reg_size = ALPHA_THREAD_STATE_COUNT; + thread_get_state (thread, ALPHA_THREAD_STATE, + (thread_state_t) ®s, ®_size); + regs.r30 = (natural_t) arg_pos; + regs.pc = (natural_t) startpc; + thread_set_state (thread, ALPHA_THREAD_STATE, + (thread_state_t) ®s, reg_size); + } +#else +# error needs to be ported +#endif + + thread_resume (thread); + mach_port_deallocate (mach_task_self (), thread); + return 0; +} |