BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / task / exec.c

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
diff --git a/task/exec.c b/task/exec.c
new file mode 100644
index 0000000..dffc92b
--- /dev/null
+++ b/task/exec.c
@@ -0,0 +1,321 @@
+/*
+ * This file deals with loading programs from the Kernel File System.  The
+ * programs in KernelFS are statically linked, so they just need to be copied
+ * into their address space and run.  Since KernelFS is present in memory, this
+ * process is very simple.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "../proc/proc.h"
+#include "../task/task.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/vm.h"
+#include "../vfs/vfs.h"
+
+#define KFS_START ((void *) 0x100000)
+
+/* Structure of a KernelFS file listing */
+typedef struct KFSEntry {
+	char name[27];
+	uint8_t size;
+	uint32_t start;
+} __attribute__((packed)) KFSEntry;
+
+/* ELF File Header */
+typedef struct ELFHeader {
+	char magic[4];
+	uint8_t arch;
+	uint8_t endian;
+	uint8_t headerVersion;
+	uint8_t osAbi;
+	uint8_t reserved[8];
+	uint16_t type;
+	uint16_t isa;
+	uint32_t version;
+	uint32_t entry;
+	uint32_t programHeader;
+	uint32_t sectionHeader;
+	uint32_t flags;
+	uint16_t headerSize;
+	uint16_t programEntrySize;
+	uint16_t numProgramEntries;
+	uint16_t sectionEntrySize;
+	uint16_t numSectionEntries;
+	uint16_t sectionNames;
+} ELFHeader;
+
+/* ELF Section Header */
+typedef struct SectionHeader {
+	uint32_t name;
+	uint32_t type;
+	uint32_t flags;
+	uint32_t address;
+	uint32_t offset;
+	uint32_t size;
+	uint32_t link;
+	uint32_t info;
+	uint32_t align;
+	uint32_t entrySize;
+} SectionHeader;
+
+/* ELF Program Header */
+typedef struct ProgramHeader {
+	uint32_t type;
+	uint32_t offset;
+	uint32_t address;
+	uint32_t reserved;
+	uint32_t filesz;
+	uint32_t memsz;
+	uint32_t flags;
+	uint32_t align;
+} ProgramHeader;
+
+static KFSEntry *
+get_file_by_name(char *name)
+{
+	KFSEntry *index;
+	for (index = KFS_START;
+	     index < (KFSEntry *) (KFS_START + 512);
+	     index++) {
+		if (!strcmp(name, index->name))
+			return index;
+	}
+	return (KFSEntry *) 0;
+}
+
+/* Get SectionHeader by index */
+static SectionHeader *
+section_header(ELFHeader *header, uint16_t index)
+{
+	SectionHeader *section;
+	section = (void *) ((char *) header + header->sectionHeader +
+	          (index * header->sectionEntrySize));
+	return section;
+}
+
+/* Get a Section name */
+static char *
+section_name(ELFHeader *header, uint32_t index)
+{
+	char *data = (char *) header + section_header(header,
+	             header->sectionNames)->offset;
+	return data + index;
+}
+
+/* Get ProgramHeader by index */
+static ProgramHeader *
+program_header(ELFHeader *header, uint16_t index)
+{
+	ProgramHeader *program;
+	program = (void *) ((char *) header + header->programHeader +
+	          (index * header->programEntrySize));
+	return program;
+}
+
+extern TaskQueue tasks;
+
+/* Execute a program */
+int
+execve(const char *file, char *argv[], char *envp[])
+{
+	if (current->tid != current->tgid) {
+		/*
+		 * TODO: This should execute the program to execute in the
+		 * "thread group leader" (the Task where tid = current->tgid)
+		 * and all other threads in the group should be exited.
+		 */
+		return -EFAULT;
+	}
+
+	if (!verify_access(file, strnlen(file, PATH_MAX), PROT_READ))
+		return -EFAULT;
+
+	/* Count argv and envp */
+	int argc, argi, envc, envi;
+	if (argv == NULL) argc = 0;
+	else for (argc = 0; argv[argc] != NULL; argc++);
+	if (envp == NULL) envc = 0;
+	else for (envc = 0; envp[envc] != NULL; envc++);
+	/* Find size of argv and envp strings */
+	size_t ssz = sizeof(int)
+	           + (sizeof(uintptr_t) * (argc + 1))
+	           + (sizeof(uintptr_t) * (envc + 1));
+	for (argi = 0; argi < argc; argi++) {
+		if (!verify_access(argv[argi], strlen(argv[argi]), PROT_READ))
+			return -EFAULT;
+		ssz += strlen(argv[argi]) + 1;
+	}
+	for (envi = 0; envi < envc; envi++) {
+		if (!verify_access(envp[envi], strlen(envp[envi]), PROT_READ))
+			return -EFAULT;
+		ssz += strlen(envp[envi]) + 1;
+	}
+
+	/* Read ELF header */
+	ELFHeader header;
+	int fd = open(file, O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	current->inSyscall = 0;
+
+	/* Only execute regular files */
+	if (!S_ISREG(current->files->fd[fd]->mode)) {
+		close(fd);
+		return -EACCES;
+	}
+
+	/* Read file header */
+	read(fd, &header, sizeof(ELFHeader));
+	if (memcmp(header.magic, "\x7F""ELF", 4) || header.isa != 3) {
+		close(fd);
+		return -ENOEXEC;
+	}
+	if (header.type != 2) { /* 1: relocatable, 2: executable */
+		close(fd);
+		return -ENOEXEC;
+	}
+
+	/*
+	 * POINT OF NO RETURN
+	 */
+
+	/* Set process name */
+	kfree(current->name);
+	current->name = kmalloc(strlen(file)+1);
+	memcpy(current->name, file, strlen(file)+1);
+
+	/* Store everything (pointers adjusted) in temporary buffer */
+	uintptr_t esp = 0xE0000000 - ssz;
+	char *istack = kmalloc(ssz);
+	char *isp = istack + ssz;
+	for (envi = envc - 1; envi >= 0; envi--) {
+		isp -= strlen(envp[envi]) + 1;
+		memcpy(isp, envp[envi], strlen(envp[envi]) + 1);
+		envp[envi] = (char *) (isp - istack) + esp;
+	}
+	if (envp)
+		envp[envc] = NULL;
+	for (argi = argc - 1; argi >= 0; argi--) {
+		isp -= strlen(argv[argi]) + 1;
+		memcpy(isp, argv[argi], strlen(argv[argi]) + 1);
+		argv[argi] = (char *) (isp - istack) + esp;
+	}
+	if (argv)
+		argv[argc] = NULL;
+	isp -= sizeof(uintptr_t);
+	*((uintptr_t *) isp ) = (uintptr_t) NULL;
+	isp -= sizeof(uintptr_t) * envc;
+	memcpy(isp, envp, sizeof(uintptr_t) * envc);
+	isp -= sizeof(uintptr_t);
+	*((uintptr_t *) isp ) = (uintptr_t) NULL;
+	isp -= sizeof(uintptr_t) * argc;
+	memcpy(isp, argv, sizeof(uintptr_t) * argc);
+	isp -= sizeof(int);
+	*((int *) isp) = argc;
+
+	/* Destroy previous executable */
+	VMRegion *head;
+	for (head = current->vm->regions; head; head = head->next)
+		vm_destroy_region(head);
+
+	/* Program headers */
+	size_t p;
+	off_t off;
+	uintptr_t pgbrk, heapEnd;
+	ProgramHeader ph, tlsph;
+	memset(&tlsph, 0, sizeof(ProgramHeader));
+	for (p = 0; p < header.numProgramEntries; p++) {
+		off = header.programHeader + (p * header.programEntrySize);
+		lseek(fd, off, 0);
+		read(fd, &ph, sizeof(ProgramHeader));
+		if (ph.type != 1) {
+			if (ph.type == 7)
+				memcpy(&tlsph, &ph, sizeof(ProgramHeader));
+			continue;
+		}
+
+		/* Map data into region */
+		mmap((void *) ph.address, ph.filesz, ph.flags,
+		     MAP_PRIVATE, fd, ph.offset & ~0xFFF);
+		/* Space left before */
+		if (ph.address & 0xFFF) {
+			memset((void *) (ph.address & ~0xFFF), 0,
+			       ph.address - (ph.address & ~0xFFF));
+		}
+		/* Unset memory */
+		if (ph.memsz > ph.filesz) {
+			pgbrk = (ph.address + ph.filesz + 0xFFF) & ~0xFFF;
+			memset((void *) (ph.address + ph.filesz), 0,
+			       pgbrk - ph.address - ph.filesz);
+			if (ph.memsz > pgbrk - ph.address)
+				mmap((void *) pgbrk,
+				     ph.memsz - (pgbrk - ph.address),
+				     ph.flags, MAP_PRIVATE | MAP_ANONYMOUS,
+				     -1, 0);
+		}
+		if (ph.address + ph.memsz > heapEnd)
+			heapEnd = ph.address + ph.memsz;
+	}
+
+	/* Store executable */
+	current->executable = file_get(current->files->fd[fd]);
+	close(fd);
+
+	/* Thread Local Storage */
+	/* FIXME */
+	if (current->tls)
+		vm_destroy_region(current->tls);
+	if (tlsph.type == 7) {
+		/* should be filesz not memsz */
+		tlsph.flags |= PROT_WRITE;
+		current->tls = vm_create_region((void *) ((heapEnd + 0xFFF) & ~0xFFF),
+		                                tlsph.memsz + 4, tlsph.flags,
+		                                MAP_PRIVATE | MAP_ANONYMOUS,
+		                                0, NULL);
+//		                                tlsph.offset, current->executable);
+		vm_remove_region(current->tls);
+		*((uint32_t *) current->tls->start + 1) = current->tls->start + 4;
+		if (tlsph.filesz)
+			memcpy((void *) current->tls->start,
+			       (void *) tlsph.address, tlsph.filesz);
+	}
+
+	/* Stack area */
+	VMRegion *oldstack = current->stack;
+	current->stack = vm_create_region((void *) 0xDFC00000, 0x400000,
+	                                  PROT_READ | PROT_WRITE,
+	                                  MAP_PRIVATE | MAP_ANONYMOUS, 0, NULL);
+	vm_remove_region(current->stack);
+	if (oldstack)
+		vm_destroy_region(oldstack);
+
+	memcpy((void *) esp, istack, ssz);
+	kfree(istack);
+
+	/* Switch to user-mode */
+	asm volatile(
+		"cli;"
+		"mov $0x23, %%ax;"
+		"mov %%ax, %%ds;"
+		"mov %%ax, %%es;"
+		"mov %%ax, %%fs;"
+		"mov %%ax, %%gs;"
+		"mov %%esi, %%eax;" // "movl %%esp, %%eax;"
+		"pushl $0x23;"
+		"pushl %%eax;"
+		"pushf;"
+		"pop %%eax;"
+		"or $0x200, %%eax;" /* Enable interrupts */
+		"push %%eax;"
+		"pushl $0x1B;"
+		"pushl %%ebx;"
+		"iret;"
+		: : "b" (header.entry), "S" (esp)
+	);
+	/* UNREACHED */
+}