diff options
Diffstat (limited to 'kernel/old/user.c')
-rw-r--r-- | kernel/old/user.c | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/kernel/old/user.c b/kernel/old/user.c new file mode 100644 index 0000000..5759534 --- /dev/null +++ b/kernel/old/user.c @@ -0,0 +1,929 @@ +/** +** @file user.c +** +** @author CSCI-452 class of 20245 +** +** @brief User-level code manipulation routines +*/ + +#define KERNEL_SRC + +#include <common.h> + +#include <bootstrap.h> +#include <elf.h> +#include <user.h> +#include <vm.h> + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +/* +** Location of the "user blob" in memory. +** +** These variables are filled in by the code in startup.S using values +** passed to it from the bootstrap. +** +** These are visible so that the startup code can find them. +*/ +uint16_t user_offset; // byte offset from the segment base +uint16_t user_segment; // segment base address +uint16_t user_sectors; // number of 512-byte sectors it occupies + +header_t *user_header; // filled in by the user_init routine +prog_t *prog_table; // filled in by the user_init routine + +/* +** PRIVATE FUNCTIONS +*/ + +#if TRACING_ELF + +/* +** This is debugging support code; if not debugging the ELF +** handling code, it won't be compiled into the kernel. +*/ + +// buffer used by some of these functions +static char ebuf[16]; + +/* +** File header functions +*/ + +// interpret the file class +static const char *fh_eclass(e32_si class) +{ + switch (class) { + case ELF_CLASS_NONE: + return ("None"); + break; + case ELF_CLASS_32: + return ("EC32"); + break; + case ELF_CLASS_64: + return ("EC64"); + break; + } + return ("????"); +} + +// interpret the data encoding +static const char *fh_edata(e32_si data) +{ + switch (data) { + case ELF_DATA_NONE: + return ("Invd"); + break; + case ELF_DATA_2LSB: + return ("2CLE"); + break; + case ELF_DATA_2MSB: + return ("2CBE"); + break; + } + return ("????"); +} + +// interpret the file type +static const char *fh_htype(e32_h type) +{ + switch (type) { + case ET_NONE: + return ("none"); + break; + case ET_REL: + return ("rel"); + break; + case ET_EXEC: + return ("exec"); + break; + case ET_DYN: + return ("dyn"); + break; + case ET_CORE: + return ("core"); + break; + default: + if (type >= ET_LO_OS && type <= ET_HI_OS) + return ("OSsp"); + else if (type >= ET_LO_CP && type <= ET_HI_CP) + return ("CPsp"); + } + sprint(ebuf, "0x%04x", type); + return ((const char *)ebuf); +} + +// interpret the machine type +static const char *fh_mtype(e32_h machine) +{ + switch (machine) { + case EM_NONE: + return ("None"); + break; + case EM_386: + return ("386"); + break; + case EM_ARM: + return ("ARM"); + break; + case EM_X86_64: + return ("AMD64"); + break; + case EM_AARCH64: + return ("AARCH64"); + break; + case EM_RISCV: + return ("RISC-V"); + break; + } + return ("Other"); +} + +// dump the program header +static void dump_fhdr(elfhdr_t *hdr) +{ + cio_puts("File header: magic "); + for (int i = EI_MAG0; i <= EI_MAG3; ++i) + put_char_or_code(hdr->e_ident.bytes[i]); + cio_printf(" class %s", fh_eclass(hdr->e_ident.f.class)); + cio_printf(" enc %s", fh_edata(hdr->e_ident.f.data)); + cio_printf(" ver %u\n", hdr->e_ident.f.version); + cio_printf(" type %s", fh_htype(hdr->e_type)); + cio_printf(" mach %s", fh_mtype(hdr->e_machine)); + cio_printf(" vers %d", hdr->e_version); + cio_printf(" entr %08x\n", hdr->e_entry); + + cio_printf(" phoff %08x", hdr->e_phoff); + cio_printf(" shoff %08x", hdr->e_shoff); + cio_printf(" flags %08x", (uint32_t)hdr->e_flags); + cio_printf(" ehsize %u\n", hdr->e_ehsize); + cio_printf(" phentsize %u", hdr->e_phentsize); + cio_printf(" phnum %u", hdr->e_phnum); + cio_printf(" shentsize %u", hdr->e_shentsize); + cio_printf(" shnum %u", hdr->e_shnum); + cio_printf(" shstrndx %u\n", hdr->e_shstrndx); +} + +/* +** Program header functions +*/ + +// categorize the header type +static const char *ph_type(e32_w type) +{ + switch (type) { + case PT_NULL: + return ("Unused"); + break; + case PT_LOAD: + return ("Load"); + break; + case PT_DYNAMIC: + return ("DLI"); + break; + case PT_INTERP: + return ("Interp"); + break; + case PT_NOTE: + return ("Aux"); + break; + case PT_SHLIB: + return ("RSVD"); + break; + case PT_PHDR: + return ("PTentry"); + break; + case PT_TLS: + return ("TLS"); + break; + default: + if (type >= PT_LO_OS && type <= PT_HI_OS) + return ("OSsp"); + else if (type >= PT_LO_CP && type <= PT_HI_CP) + return ("CPsp"); + } + sprint(ebuf, "0x%08x", type); + return ((const char *)ebuf); +} + +// report the individual flags +static void ph_flags(e32_w flags) +{ + if ((flags & PF_R) != 0) + cio_putchar('R'); + if ((flags & PF_W) != 0) + cio_putchar('W'); + if ((flags & PF_E) != 0) + cio_putchar('X'); +} + +// dump a program header +static void dump_phdr(elfproghdr_t *hdr, int n) +{ + cio_printf("Prog header %d, type %s\n", n, ph_type(hdr->p_type)); + cio_printf(" offset %08x", hdr->p_offset); + cio_printf(" va %08x", hdr->p_va); + cio_printf(" pa %08x\n", hdr->p_pa); + cio_printf(" filesz %08x", hdr->p_filesz); + cio_printf(" memsz %08x", hdr->p_memsz); + cio_puts(" flags "); + ph_flags(hdr->p_flags); + cio_printf(" align %08x", hdr->p_align); + cio_putchar('\n'); +} + +/* +** Section header functions +*/ + +// interpret the header type +static const char *sh_type(e32_w type) +{ + switch (type) { + case SHT_NULL: + return ("Unused"); + break; + case SHT_PROGBITS: + return ("Progbits"); + break; + case SHT_SYMTAB: + return ("Symtab"); + break; + case SHT_STRTAB: + return ("Strtab"); + break; + case SHT_RELA: + return ("Rela"); + break; + case SHT_HASH: + return ("Hash"); + break; + case SHT_DYNAMIC: + return ("Dynamic"); + break; + case SHT_NOTE: + return ("Note"); + break; + case SHT_NOBITS: + return ("Nobits"); + break; + case SHT_REL: + return ("Rel"); + break; + case SHT_SHLIB: + return ("Shlib"); + break; + case SHT_DYNSYM: + return ("Dynsym"); + break; + default: + if (type >= SHT_LO_CP && type <= SHT_HI_CP) + return ("CCsp"); + else if (type >= SHT_LO_US && type <= SHT_HI_US) + return ("User"); + } + sprint(ebuf, "0x%08x", type); + return ((const char *)ebuf); +} + +// report the various flags +static void sh_flags(unsigned int flags) +{ + if ((flags & SHF_WRITE) != 0) + cio_putchar('W'); + if ((flags & SHF_ALLOC) != 0) + cio_putchar('A'); + if ((flags & SHF_EXECINSTR) != 0) + cio_putchar('X'); + if ((flags & SHF_MERGE) != 0) + cio_putchar('M'); + if ((flags & SHF_STRINGS) != 0) + cio_putchar('S'); + if ((flags & SHF_INFO_LINK) != 0) + cio_putchar('L'); + if ((flags & SHF_LINK_ORDER) != 0) + cio_putchar('o'); + if ((flags & SHF_OS_NONCON) != 0) + cio_putchar('n'); + if ((flags & SHF_GROUP) != 0) + cio_putchar('g'); + if ((flags & SHF_TLS) != 0) + cio_putchar('t'); +} + +// dump a section header +ATTR_UNUSED +static void dump_shdr(elfsecthdr_t *hdr, int n) +{ + cio_printf("Sect header %d, type %d (%s), name %s\n", n, hdr->sh_type, + sh_type(hdr->sh_type)); + cio_printf(" flags %08x ", (uint32_t)hdr->sh_flags); + sh_flags(hdr->sh_flags); + cio_printf(" addr %08x", hdr->sh_addr); + cio_printf(" offset %08x", hdr->sh_offset); + cio_printf(" size %08x\n", hdr->sh_size); + cio_printf(" link %08x", hdr->sh_link); + cio_printf(" info %08x", hdr->sh_info); + cio_printf(" align %08x", hdr->sh_addralign); + cio_printf(" entsz %08x\n", hdr->sh_entsize); +} +#endif + +/** +** read_phdrs(addr,phoff,phentsize,phnum) +** +** Parses the ELF program headers and each segment described into memory. +** +** @param hdr Pointer to the program header +** @param pcb Pointer to the PCB (and its PDE) +** +** @return status of the attempt: +** SUCCESS everything loaded correctly +** E_LOAD_LIMIT more than N_LOADABLE PT_LOAD sections +** other status returned from vm_add() +*/ +static int read_phdrs(elfhdr_t *hdr, pcb_t *pcb) +{ + // sanity check + assert1(hdr != NULL); + assert2(pcb != NULL); + +#if TRACING_USER + cio_printf("read_phdrs(%08x,%08x)\n", (uint32_t)hdr, (uint32_t)pcb); +#endif + + // iterate through the program headers + uint_t nhdrs = hdr->e_phnum; + + // pointer to the first header table entry + elfproghdr_t *curr = (elfproghdr_t *)((uint32_t)hdr + hdr->e_phoff); + + // process them all + int loaded = 0; + for (uint_t i = 0; i < nhdrs; ++i, ++curr) { +#if TRACING_ELF + dump_phdr(curr, i); +#endif + if (curr->p_type != PT_LOAD) { + // not loadable --> we'll skip it + continue; + } + + if (loaded >= N_LOADABLE) { +#if TRACING_USER + cio_puts(" LIMIT\n"); +#endif + return E_LOAD_LIMIT; + } + + // set a pointer to the bytes within the object file + char *data = (char *)(((uint32_t)hdr) + curr->p_offset); +#if TRACING_USER + cio_printf(" data @ %08x", (uint32_t)data); +#endif + + // copy the pages into memory + int stat = vm_add(pcb->pdir, curr->p_flags & PF_W, false, + (char *)curr->p_va, curr->p_memsz, data, + curr->p_filesz); + if (stat != SUCCESS) { + // TODO what else should we do here? check for memory leak? + return stat; + } + + // set the section table entry in the PCB + pcb->sects[loaded].length = curr->p_memsz; + pcb->sects[loaded].addr = curr->p_va; +#if TRACING_USER + cio_printf(" loaded %u @ %08x\n", pcb->sects[loaded].length, + pcb->sects[loaded].addr); +#endif + ++loaded; + } + + return SUCCESS; +} + +/** +** Name: stack_setup +** +** Set up the stack for a new process +** +** @param pcb Pointer to the PCB for the process +** @param entry Entry point for the new process +** @param args Argument vector to be put in place +** @param sys Is the argument vector from kernel code? +** +** @return A (user VA) pointer to the context_t on the stack, or NULL +*/ +static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, + bool_t sys) +{ +#if TRACING_USER + cio_printf("stksetup: pcb %08x, entry %08x, args %08x\n", (uint32_t)pcb, + entry, (uint32_t)args); +#endif + + /* + ** First, we need to calculate the space we'll need for the argument + ** vector and strings. + ** + ** Keeping track of kernel vs. user VAs is tricky, so we'll use + ** a prefix on variable names: kv_* is a kernel virtual address; + ** uv_* is a user virtual address. + ** + ** We rely on the C standard, section 6.7.8, to clear these arrays: + ** + ** "21 If there are fewer initializers in a brace-enclosed list + ** than there are elements or members of an aggregate, or + ** fewer characters in a string literal used to initialize an + ** array of known size than there are elements in the array, + ** the remainder of the aggregate shall be initialized + ** implicitly the same as objects that have static storage + ** duration." + */ + + int argbytes = 0; // total length of arg strings + int argc = 0; // number of argv entries + const char *kv_strs[N_ARGS] = { 0 }; // converted user arg string pointers + int strlengths[N_ARGS] = { 0 }; // length of each string + const char *uv_argv[N_ARGS] = { 0 }; // argv pointers + + /* + ** IF the argument list given to us came from user code, we need + ** to convert its address and the addresses it contains to kernel + ** VAs; otherwise, we can use them directly. + */ + char **kv_args = sys ? args : vm_uva2kva(pcb->pdir, (void *)args); + + while (kv_args[argc] != NULL) { + kv_strs[argc] = sys ? args[argc] : + vm_uva2kva(pcb->pdir, (void *)(kv_args[argc])); + strlengths[argc] = strlen(kv_strs[argc]) + 1; + // can't go over one page in size + if ((argbytes + strlengths[argc]) > SZ_PAGE) { + // oops - ignore this and any others + break; + } + argbytes += strlengths[argc]; + ++argc; + } + + // Round up the byte count to the next multiple of four. + argbytes = (argbytes + 3) & MOD4_MASK; + + /* + ** The pages for the stack were cleared when they were allocated, + ** so we don't need to remember to do that. + ** + ** We reserve one longword at the bottom of the stack to hold a + ** pointer to where argv is on the stack. + ** + ** The user code was linked with a startup function that defines + ** the entry point (_start), calls main(), and then calls exit() + ** if main() returns. We need to set up the stack this way: + ** + ** esp -> context <- context save area + ** ... <- context save area + ** context <- context save area + ** entry_pt <- return address for the ISR + ** argc <- argument count for main() + ** /-> argv <- argv pointer for main() + ** | ... <- argv array w/trailing NULL + ** | ... <- argv character strings + ** \--- ptr <- last word in stack + ** + ** Stack alignment rules for the SysV ABI i386 supplement dictate that + ** the 'argc' parameter must be at an address that is a multiple of 16; + ** see below for more information. + ** + ** Ultimately, this is what the bottom end of the stack will look like: + ** + ** kvavptr + ** kvacptr | + ** | | + ** v v + ** argc argv av[0] av[1] etc NULL str0 str1 etc. + ** [....][....][....][....] ... [0000] ... [......0......0.........] + ** | ^ | | ^ ^ + ** | | | | | | + ** ------ | ---------------------|------- + ** --------------------------- + */ + + /* + ** We need to find the last page of the user stack. Find the page + ** table for the 4MB user address space. The physical address of its + ** frame is in the first page directory entry. Extract that from the + ** entry and convert it into a virtual address for the kernel to use. + */ + pde_t *kv_userpt = (pde_t *)P2V(PTE_ADDR(pcb->pdir[USER_PDE])); + assert(kv_userpt != NULL); + + /* + ** The final entries in that PMT are for the pages of the user stack. + ** Grab the physical address of the frame for the last one. (Again, + ** we need to convert it to a virtual address we can use.) + */ + + // the PMT entry for that page + pte_t pmt_entry = kv_userpt[USER_STK_LAST_PTE]; + assert(IS_PRESENT(pmt_entry)); + + // user VA for the first byte of that page + uint32_t *uvptr = (uint32_t *)USER_STACK_P2; + + // convert that address to a kernel VA + uint32_t *kvptr = (uint32_t *)vm_uva2kva(pcb->pdir, (void *)uvptr); + + /* + ** Move these pointers to where the string area will begin. We + ** will then back up to the next lower multiple-of-four address. + */ + + uint32_t uvstrptr = ((uint32_t)uvptr) + SZ_PAGE - argbytes; + uvstrptr &= MOD4_MASK; + + uint32_t kvstrptr = ((uint32_t)kvptr) + SZ_PAGE - argbytes; + kvstrptr &= MOD4_MASK; + + // Copy over the argv strings, remembering where each string begins + for (int i = 0; i < argc; ++i) { + // copy the string using kernel addresses + strcpy((char *)kvstrptr, kv_args[i]); + + // remember the user address where this string went + uv_argv[i] = (char *)uvstrptr; + + // adjust both string addresses + kvstrptr += strlengths[i]; + uvstrptr += strlengths[i]; + } + + /* + ** Next, we need to copy over the other data. Start by determining + ** where 'argc' should go. + ** + ** Stack alignment is controlled by the SysV ABI i386 supplement, + ** version 1.2 (June 23, 2016), which states in section 2.2.2: + ** + ** "The end of the input argument area shall be aligned on a 16 + ** (32 or 64, if __m256 or __m512 is passed on stack) byte boundary. + ** In other words, the value (%esp + 4) is always a multiple of 16 + ** (32 or 64) when control is transferred to the function entry + ** point. The stack pointer, %esp, always points to the end of the + ** latest allocated stack frame." + ** + ** Isn't technical documentation fun? Ultimately, this means that + ** the first parameter to main() should be on the stack at an address + ** that is a multiple of 16. In our case, that is 'argc'. + */ + + /* + ** The space needed for argc, argv, and the argv array itself is + ** argc + 3 words (argc+1 for the argv entries, plus one word each + ** for argc and argv). We back up that much from the string area. + */ + + int nwords = argc + 3; + uint32_t *kvacptr = ((uint32_t *)kvstrptr) - nwords; + uint32_t *uvacptr = ((uint32_t *)uvstrptr) - nwords; + + // back these up to multiple-of-16 addresses for stack alignment + kvacptr = (uint32_t *)(((uint32_t)kvacptr) & MOD16_MASK); + uvacptr = (uint32_t *)(((uint32_t)uvacptr) & MOD16_MASK); + + // copy in 'argc' + *kvacptr = argc; + + // 'argv' immediately follows 'argc', and 'argv[0]' immediately + // follows 'argv' + uint32_t *kvavptr = kvacptr + 2; + *(kvavptr - 1) = (uint32_t)kvavptr; + + // now, the argv entries themselves + for (int i = 0; i < argc; ++i) { + *kvavptr++ = (uint32_t)uv_argv[i]; + } + + // and the trailing NULL + *kvavptr = NULL; + + /* + ** Almost done! + ** + ** Now we need to set up the initial context for the executing + ** process. + ** + ** When this process is dispatched, the context restore code will + ** pop all the saved context information off the stack, including + ** the saved EIP, CS, and EFLAGS. We set those fields up so that + ** the interrupt "returns" to the entry point of the process. + */ + + // Locate the context save area on the stack by backup up one + // "context" from where the argc value is saved + context_t *kvctx = ((context_t *)kvacptr) - 1; + uint32_t uvctx = (uint32_t)(((context_t *)uvacptr) - 1); + + /* + ** We cleared the entire stack earlier, so all the context + ** fields currently contain zeroes. We now need to fill in + ** all the important fields. + ** + ** Note: we don't need to set the ESP value for the process, + ** as the 'popa' that restores the general registers doesn't + ** actually restore ESP from the context area - it leaves it + ** where it winds up. + */ + + kvctx->eflags = DEFAULT_EFLAGS; // IF enabled, IOPL 0 + kvctx->eip = entry; // initial EIP + kvctx->cs = GDT_CODE; // segment registers + kvctx->ss = GDT_STACK; + kvctx->ds = kvctx->es = kvctx->fs = kvctx->gs = GDT_DATA; + + /* + ** Return the new context pointer to the caller as a user + ** space virtual address. + */ + + return ((context_t *)uvctx); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: user_init +** +** Initializes the user support module. +*/ +void user_init(void) +{ +#if TRACING_INIT + cio_puts(" User"); +#endif + + // This is gross, but we need to get this information somehow. + // Access the "user blob" data in the second bootstrap sector + uint16_t *blobdata = (uint16_t *)P2V(USER_BLOB_DATA); + user_offset = *blobdata++; + user_segment = *blobdata++; + user_sectors = *blobdata++; + +#if TRACING_USER + cio_printf("\nUser blob: %u sectors @ %04x:%04x", user_sectors, + user_segment, user_offset); +#endif + + // calculate the location of the user blob + if (user_sectors > 0) { + // calculate the address of the header + user_header = (header_t *)(KERN_BASE + ((((uint_t)user_segment) << 4) + + ((uint_t)user_offset))); + + // the program table immediate follows the blob header + prog_table = (prog_t *)(user_header + 1); + +#if TRACING_USER + cio_printf(", hdr %08x, %u progs, tbl %08x\n", (uint32_t)user_header, + user_header->num, (uint32_t)prog_table); +#endif + + } else { + // too bad, so sad! + user_header = NULL; + prog_table = NULL; +#if TRACING_USER + cio_putchar('\n'); +#endif + } +} + +/** +** Name: user_locate +** +** Locates a user program in the user code archive. +** +** @param what The ID of the user program to find +** +** @return pointer to the program table entry in the code archive, or NULL +*/ +prog_t *user_locate(uint_t what) +{ +#if TRACING_USER + cio_printf("ulocate: %u\n", what); +#endif + + // no programs if there is no blob! + if (user_header == NULL) { + return NULL; + } + + // make sure this is a reasonable program to request + if (what >= user_header->num) { + // no such program! + return NULL; + } + + // find the entry in the program table + prog_t *prog = &prog_table[what]; + + // if there are no bytes, it's useless + if (prog->size < 1) { + return NULL; + } + + // return the program table pointer + return prog; +} + +/** +** Name: user_duplicate +** +** Duplicates the memory setup for an existing process. +** +** @param new The PCB for the new copy of the program +** @param old The PCB for the existing the program +** +** @return the status of the duplicate attempt +*/ +int user_duplicate(pcb_t *new, pcb_t *old) +{ +#if TRACING_USER + cio_printf("udup: old %08x new %08x\n", (uint32_t)old, (uint32_t)new); +#endif + + // We need to do a recursive duplication of the process address + // space of the current process. First, we create a new user + // page directory. Next, we'll duplicate the USER_PDE page + // table. Finally, we'll go through that table and duplicate + // all the frames. + + // create the initial VM hierarchy + pde_t *pdir = vm_mkuvm(); + if (pdir == NULL) { + return E_NO_MEMORY; + } + new->pdir = pdir; + + // Next, add a USER_PDE page table that's a duplicate of the + // current process' page table + if (!vm_uvmdup(new->pdir, old->pdir)) { + // check for memory leak? + return E_NO_MEMORY; + } + + // We don't do copy-on-write, so we must duplicate all the + // individual page frames. Iterate through all the user-level + // PDE entries, and replace the existing frames with duplicates. + // + // NOTE: we only deal with pdir[0] here, as we are limiting + // the user address space to the first 4MB. If the size of + // the address space goes up, this code will need to be + // modified to loop over the larger space. + + // pointer to the PMT for the user + pte_t *pt = (pte_t *)(pdir[USER_PDE]); + assert(pt != NULL); + + for (int i = 0; i < N_PTE; ++i) { + // get the current entry from the PMT + pte_t entry = *pt; + + // if this entry is present, + if (IS_PRESENT(entry)) { + // duplicate the frame pointed to by this PTE + void *tmp = vm_pagedup((void *)PTE_ADDR(entry)); + + // replace the old frame number with the new one + *pt = (pte_t)(((uint32_t)tmp) | PERMS(entry)); + + } else { + *pt = 0; + } + ++pt; + } + + return SUCCESS; +} + +/** +** Name: user_load +** +** Loads a user program from the user code archive into memory. +** Allocates all needed frames and sets up the VM tables. +** +** @param ptab A pointer to the program table entry to be loaded +** @param pcb The PCB for the program being loaded +** @param args The argument vector for the program +** @param sys Is the argument vector from kernel code? +** +** @return the status of the load attempt +*/ +int user_load(prog_t *ptab, pcb_t *pcb, const char **args, bool_t sys) +{ + // NULL pointers are bad! + assert1(ptab != NULL); + assert1(pcb != NULL); + assert1(args != NULL); + +#if TRACING_USER + cio_printf("Uload: prog '%s' pcb %08x args %08x\n", + ptab->name[0] ? ptab->name : "?", (uint32_t)pcb, (uint32_t)args); +#endif + + // locate the ELF binary + elfhdr_t *hdr = (elfhdr_t *)((uint32_t)user_header + ptab->offset); + +#if TRACING_ELF + cio_printf("Load: ptab %08x: '%s', off %08x, size %08x, flags %08x\n", + (uint32_t)ptab, ptab->name, ptab->offset, ptab->size, + ptab->flags); + cio_printf(" args %08x:", (uint32_t)args); + if (sys) { + for (int i = 0; args[i] != NULL; ++i) { + cio_printf(" [%d] %s", i, args[i]); + } + } else { + char **kv_args = vm_uva2kva(pcb->pdir, args); + for (int i = 0; kv_args[i] != NULL; ++i) { + cio_printf(" [%d] %s", i, + (char *)vm_uva2kva(pcb->pdir, kv_args[i])); + } + } + cio_printf("\n pcb %08x (pid %u)\n", (uint32_t)pcb, pcb->pid); + dump_fhdr(hdr); +#endif + + // verify the ELF header + if (hdr->e_ident.f.magic != ELF_MAGIC) { + return E_BAD_PARAM; + } + + // allocate a page directory + pcb->pdir = vm_mkuvm(); + if (pcb->pdir == NULL) { + return E_NO_MEMORY; + } + + // read all the program headers + int stat = read_phdrs(hdr, pcb); + if (stat != SUCCESS) { + cio_printf("Uload: read_phdrs('%s') returned %d\n", ptab->name, stat); + PANIC(0, "User_load: phdr read failed"); + } + + // next, set up the runtime stack - just like setting up loadable + // sections, except nothing to copy + stat = + vm_add(pcb->pdir, true, false, (void *)USER_STACK, SZ_USTACK, NULL, 0); + if (stat != SUCCESS) { + cio_printf("Uload: vm_add('%s') stack returned %d\n", ptab->name, stat); + PANIC(0, "user_load: vm_add stack failed"); + } + + // set up the command-line arguments + pcb->context = stack_setup(pcb, hdr->e_entry, args, sys); + + return SUCCESS; +} + +/** +** Name: user_cleanup +** +** "Unloads" a user program. Deallocates all memory frames and +** cleans up the VM structures. +** +** @param pcb The PCB of the program to be unloaded +*/ +void user_cleanup(pcb_t *pcb) +{ +#if TRACING_USER + cio_printf("Uclean: %08x\n", (uint32_t)pcb); +#endif + + if (pcb == NULL) { + // should this be an error? + return; + } + + vm_free(pcb->pdir); + pcb->pdir = NULL; +} |