BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / task / exec.c

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
/*
 * 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 */
}