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