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