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 */ +}