summaryrefslogtreecommitdiff
path: root/serverboot/load.c
diff options
context:
space:
mode:
Diffstat (limited to 'serverboot/load.c')
-rw-r--r--serverboot/load.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/serverboot/load.c b/serverboot/load.c
new file mode 100644
index 00000000..fc16baf1
--- /dev/null
+++ b/serverboot/load.c
@@ -0,0 +1,554 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <assert.h>
+#include <mach/mach_interface.h>
+#include <varargs.h>
+#include "mach-exec.h"
+#include "../boot/boot_script.h"
+
+#include <file_io.h>
+
+
+boolean_t load_protect_text = TRUE;
+
+
+struct stuff
+{
+ struct file *fp;
+ task_t user_task;
+
+ /* uncompressed image */
+ vm_offset_t image_addr;
+ vm_size_t image_size;
+
+ vm_offset_t aout_symtab_ofs;
+ vm_size_t aout_symtab_size;
+ vm_offset_t aout_strtab_ofs;
+ vm_size_t aout_strtab_size;
+};
+
+char *set_regs(
+ mach_port_t user_task,
+ mach_port_t user_thread,
+ struct exec_info *info,
+ int arg_size);
+
+static void read_symtab_from_file(
+ struct file *fp,
+ mach_port_t host_port,
+ task_t task,
+ char * symtab_name,
+ struct stuff *st);
+
+/* Callback functions for reading the executable file. */
+static int prog_read(void *handle, vm_offset_t file_ofs, void *buf, vm_size_t size,
+ vm_size_t *out_actual)
+{
+ struct stuff *st = handle;
+ vm_size_t resid;
+ int result;
+
+ result = read_file(st->fp, file_ofs, buf, size, &resid);
+ if (result)
+ return result;
+ *out_actual = size - resid;
+ return 0;
+}
+
+static int prog_read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
+ vm_offset_t mem_addr, vm_size_t mem_size,
+ exec_sectype_t sec_type)
+{
+ struct stuff *st = handle;
+ vm_offset_t page_start = trunc_page(mem_addr);
+ vm_offset_t page_end = round_page(mem_addr + mem_size);
+ vm_prot_t mem_prot = sec_type & EXEC_SECTYPE_PROT_MASK;
+ vm_offset_t area_start;
+ int result;
+
+ if (sec_type & EXEC_SECTYPE_AOUT_SYMTAB)
+ {
+ st->aout_symtab_ofs = file_ofs;
+ st->aout_symtab_size = file_size;
+ }
+ if (sec_type & EXEC_SECTYPE_AOUT_STRTAB)
+ {
+ st->aout_strtab_ofs = file_ofs;
+ st->aout_strtab_size = file_size;
+ }
+
+ if (!(sec_type & EXEC_SECTYPE_ALLOC))
+ return 0;
+
+ assert(mem_size > 0);
+ assert(mem_size > file_size);
+
+ /*
+ printf("section %08x-%08x-%08x prot %08x (%08x-%08x)\n",
+ mem_addr, mem_addr+file_size, mem_addr+mem_size, mem_prot, page_start, page_end);
+ */
+
+ result = vm_allocate(mach_task_self(), &area_start, page_end - page_start, TRUE);
+ if (result) return (result);
+
+ if (file_size > 0)
+ {
+ vm_size_t resid;
+
+ result = read_file(st->fp, file_ofs, area_start + (mem_addr - page_start),
+ file_size, &resid);
+ if (result) return result;
+ if (resid) return EX_CORRUPT;
+ }
+
+ if (mem_size > file_size)
+ {
+ bzero((void*)area_start + (mem_addr + file_size - page_start),
+ mem_size - file_size);
+ }
+
+ result = vm_allocate(st->user_task, &page_start, page_end - page_start, FALSE);
+ if (result) return (result);
+ assert(page_start == trunc_page(mem_addr));
+
+ result = vm_write(st->user_task, page_start, area_start, page_end - page_start);
+ if (result) return (result);
+
+ result = vm_deallocate(mach_task_self(), area_start, page_end - page_start);
+ if (result) return (result);
+
+ /*
+ * Protect the segment.
+ */
+ if (load_protect_text && (mem_prot != VM_PROT_ALL)) {
+ result = vm_protect(st->user_task, page_start, page_end - page_start,
+ FALSE, mem_prot);
+ if (result) return (result);
+ }
+
+ return 0;
+}
+
+/* Callback functions for reading the uncompressed image. */
+static int image_read(void *handle, vm_offset_t file_ofs, void *buf,
+ vm_size_t size, vm_size_t *out_actual)
+{
+ struct stuff *st = handle;
+ bcopy(st->image_addr + file_ofs, buf, size);
+ *out_actual = size;
+ return 0;
+}
+
+static int image_read_exec(void *handle, vm_offset_t file_ofs,
+ vm_size_t file_size, vm_offset_t mem_addr,
+ vm_size_t mem_size, exec_sectype_t sec_type)
+{
+ struct stuff *st = handle;
+ vm_offset_t page_start = trunc_page(mem_addr);
+ vm_offset_t page_end = round_page(mem_addr + mem_size);
+ vm_prot_t mem_prot = sec_type & EXEC_SECTYPE_PROT_MASK;
+ vm_offset_t area_start;
+ int result;
+
+ if (sec_type & EXEC_SECTYPE_AOUT_SYMTAB)
+ {
+ st->aout_symtab_ofs = file_ofs;
+ st->aout_symtab_size = file_size;
+ }
+ if (sec_type & EXEC_SECTYPE_AOUT_STRTAB)
+ {
+ st->aout_strtab_ofs = file_ofs;
+ st->aout_strtab_size = file_size;
+ }
+
+ if (!(sec_type & EXEC_SECTYPE_ALLOC))
+ return 0;
+
+ assert(mem_size > 0);
+ assert(mem_size > file_size);
+
+ /*
+ printf("section %08x-%08x-%08x prot %08x (%08x-%08x)\n",
+ mem_addr, mem_addr+file_size, mem_addr+mem_size, mem_prot, page_start, page_end);
+ */
+
+ result = vm_allocate(mach_task_self(), &area_start, page_end - page_start, TRUE);
+ if (result) return (result);
+
+ if (file_size > 0)
+ {
+ bcopy(st->image_addr + file_ofs, area_start + (mem_addr - page_start),
+ file_size);
+ }
+
+ if (mem_size > file_size)
+ {
+ bzero((void*)area_start + (mem_addr + file_size - page_start),
+ mem_size - file_size);
+ }
+
+ result = vm_allocate(st->user_task, &page_start, page_end - page_start, FALSE);
+ if (result) return (result);
+ assert(page_start == trunc_page(mem_addr));
+
+ result = vm_write(st->user_task, page_start, area_start, page_end - page_start);
+ if (result) return (result);
+
+ result = vm_deallocate(mach_task_self(), area_start, page_end - page_start);
+ if (result) return (result);
+
+ /*
+ * Protect the segment.
+ */
+ if (load_protect_text && (mem_prot != VM_PROT_ALL)) {
+ result = vm_protect(st->user_task, page_start, page_end - page_start,
+ FALSE, mem_prot);
+ if (result) return (result);
+ }
+
+ return 0;
+}
+
+mach_port_t boot_script_read_file (const char *file)
+{ return MACH_PORT_NULL; } /* XXX */
+
+int
+boot_script_exec_cmd (task_t user_task,
+ char *file_name,
+ int arg_count, char **argv,
+ char *argstrings, int argslen)
+{
+ extern mach_port_t bootstrap_master_device_port, bootstrap_master_host_port;
+ extern char *root_name;
+ extern char **environ;
+ int envc, env_len;
+
+ int arg_len = argslen;
+ char *arg_pos;
+
+ kern_return_t result;
+ thread_t user_thread;
+ struct file file;
+ char namebuf[MAXPATHLEN+1];
+
+ struct stuff st;
+ struct exec_info info;
+
+ extern char * strbuild();
+
+ if (strcmp (file_name, "/dev/"))
+ (void) strbuild(namebuf, "/dev/", root_name, "/", file_name,
+ (char *)0);
+ else
+ strcpy (namebuf, file_name);
+
+ /*
+ * Open the file
+ */
+ bzero((char *)&file, sizeof(file));
+
+ result = open_file(bootstrap_master_device_port, namebuf, &file);
+ if (result != 0) {
+ panic ("%s: %s", namebuf, strerror (result));
+ }
+
+ env_len = 0;
+ for (envc = 0; environ[envc]; ++envc)
+ env_len += strlen (environ[envc]) + 1;
+
+ /*
+ * Add space for:
+ * arg_count
+ * pointers to arguments
+ * trailing 0 pointer
+ * environment variables
+ * trailing 0 pointer
+ * and align to integer boundary
+ */
+ arg_len += sizeof(integer_t) + (envc + 2 + arg_count) * sizeof(char *);
+ arg_len += env_len;
+ arg_len = (arg_len + (sizeof(integer_t) - 1)) & ~(sizeof(integer_t)-1);
+
+ /*
+ * We refrain from checking IEXEC bits to make
+ * things a little easier when things went bad.
+ * Say you have ftp(1) but chmod(1) is gone.
+ */
+ if (!file_is_regular(&file))
+ panic("boot_load_program: %s is not a regular file", namebuf);
+
+ /*
+ * Load the executable file.
+ */
+ st.fp = &file;
+ st.user_task = user_task;
+ st.aout_symtab_size = 0;
+ st.aout_strtab_size = 0;
+ result = exec_load(prog_read, prog_read_exec, &st, &info);
+#ifdef GZIP
+ if (result)
+ {
+ /*
+ * It might be gzip file.
+ */
+ int err;
+ extern int
+ serverboot_gunzip(struct file *, void **, size_t *);
+
+ err = serverboot_gunzip(st.fp,
+ &(st.image_addr),
+ &(st.image_size));
+ if (!err)
+ {
+ result = exec_load(image_read,
+ image_read_exec,
+ &st,
+ &info);
+ vm_deallocate(mach_task_self(),
+ st.image_addr,
+ st.image_size);
+ }
+ }
+#endif GZIP
+#ifdef BZIP2
+ if (result)
+ {
+ /*
+ * It might be bzip2 file.
+ */
+ int err;
+ extern int
+ serverboot_bunzip2(struct file *, void **, size_t *);
+
+ err = serverboot_bunzip2(st.fp,
+ &(st.image_addr),
+ &(st.image_size));
+ if (!err)
+ {
+ result = exec_load(image_read,
+ image_read_exec,
+ &st,
+ &info);
+ vm_deallocate(mach_task_self(),
+ st.image_addr,
+ st.image_size);
+ }
+ }
+#endif BZIP2
+ if (result)
+ panic ("cannot load %s: %s", namebuf, strerror (result));
+#if 0
+ printf("(serverboot): loaded %s; entrypoint %08x\n", namebuf, info.entry);
+#endif
+
+ /*
+ * Set up the stack and user registers.
+ */
+ result = thread_create (user_task, &user_thread);
+ if (result)
+ panic ("can't create user thread for %s: %s", namebuf,
+ strerror (result));
+ arg_pos = set_regs(user_task, user_thread, &info, arg_len);
+
+ /*
+ * Read symbols from the executable file.
+ */
+#if 0
+ printf("(serverboot): loading symbols from %s\n", namebuf);
+ read_symtab_from_file(&file, bootstrap_master_host_port, user_task, namebuf, &st);
+#endif
+
+ /*
+ * Copy out the arguments.
+ */
+ {
+ vm_offset_t u_arg_start;
+ /* user start of argument list block */
+ vm_offset_t k_arg_start;
+ /* kernel start of argument list block */
+ vm_offset_t u_arg_page_start;
+ /* user start of args, page-aligned */
+ vm_size_t arg_page_size;
+ /* page_aligned size of args */
+ vm_offset_t k_arg_page_start;
+ /* kernel start of args, page-aligned */
+
+ register
+ char ** k_ap; /* kernel arglist address */
+ char * u_cp; /* user argument string address */
+ register
+ char * k_cp; /* kernel argument string address */
+ register
+ int i;
+
+ /*
+ * Get address of argument list in user space
+ */
+ u_arg_start = (vm_offset_t)arg_pos;
+
+ /*
+ * Round to page boundaries, and allocate kernel copy
+ */
+ u_arg_page_start = trunc_page(u_arg_start);
+ arg_page_size = (vm_size_t)(round_page(u_arg_start + arg_len)
+ - u_arg_page_start);
+
+ result = vm_allocate(mach_task_self(),
+ &k_arg_page_start,
+ (vm_size_t)arg_page_size,
+ TRUE);
+ if (result)
+ panic("boot_load_program: arg size");
+
+ /*
+ * Set up addresses corresponding to user pointers
+ * in the kernel block
+ */
+ k_arg_start = k_arg_page_start + (u_arg_start - u_arg_page_start);
+
+ k_ap = (char **)k_arg_start;
+
+ /*
+ * Start the strings after the arg-count and pointers
+ */
+ u_cp = (char *)u_arg_start + arg_count * sizeof(char *)
+ + envc * sizeof(char *)
+ + 2 * sizeof(char *)
+ + sizeof(integer_t);
+ k_cp = (char *)k_arg_start + arg_count * sizeof(char *)
+ + envc * sizeof(char *)
+ + 2 * sizeof(char *)
+ + sizeof(integer_t);
+
+ /*
+ * first the argument count
+ */
+ *k_ap++ = (char *)arg_count;
+
+ /*
+ * Then the strings and string pointers for each argument
+ */
+ for (i = 0; i < arg_count; i++)
+ *k_ap++ = argv[i] - argstrings + u_cp;
+ *k_ap++ = (char *)0;
+ bcopy (argstrings, k_cp, argslen);
+ k_cp += argslen;
+ u_cp += argslen;
+
+ for (i = 0; i < envc; i++)
+ *k_ap++ = environ[i] - environ[0] + u_cp;
+ *k_ap = (char *)0;
+ bcopy (environ[0], k_cp, env_len);
+
+ /*
+ * Now write all of this to user space.
+ */
+ (void) vm_write(user_task,
+ u_arg_page_start,
+ k_arg_page_start,
+ arg_page_size);
+
+ (void) vm_deallocate(mach_task_self(),
+ k_arg_page_start,
+ arg_page_size);
+ }
+
+ /*
+ * Close the file.
+ */
+ close_file(&file);
+
+ /* Resume the thread. */
+ thread_resume (user_thread);
+ mach_port_deallocate (mach_task_self (), user_thread);
+
+ return (0);
+}
+
+/*
+ * Load symbols from file into kernel debugger.
+ */
+static void read_symtab_from_file(
+ struct file *fp,
+ mach_port_t host_port,
+ task_t task,
+ char * symtab_name,
+ struct stuff *st)
+{
+ vm_size_t resid;
+ kern_return_t result;
+ vm_size_t table_size;
+ vm_offset_t symtab;
+
+#if 0
+
+ if (!st->aout_symtab_size || !st->aout_strtab_size)
+ return;
+
+ /*
+ * Allocate space for the symbol table.
+ */
+ table_size = sizeof(vm_size_t)
+ + st->aout_symtab_size
+ + st->aout_strtab_size;
+ result= vm_allocate(mach_task_self(),
+ &symtab,
+ table_size,
+ TRUE);
+ if (result) {
+ printf("[ error %d allocating space for %s symbol table ]\n",
+ result, symtab_name);
+ return;
+ }
+
+ /*
+ * Set the symbol table length word,
+ * then read in the symbol table and string table.
+ */
+ *(vm_size_t*)symtab = st->aout_symtab_size;
+ result = read_file(fp, st->aout_symtab_ofs,
+ symtab + sizeof(vm_size_t),
+ st->aout_symtab_size + st->aout_strtab_size,
+ &resid);
+ if (result || resid) {
+ printf("[ no valid symbol table present for %s ]\n",
+ symtab_name);
+ }
+ else {
+ /*
+ * Load the symbols into the kernel.
+ */
+ result = host_load_symbol_table(
+ host_port,
+ task,
+ symtab_name,
+ symtab,
+ table_size);
+ }
+ (void) vm_deallocate(mach_task_self(), symtab, table_size);
+#endif
+}