diff --git a/include/offsets.h b/include/offsets.h index c0cc029..bf19776 100644 --- a/include/offsets.h +++ b/include/offsets.h @@ -3,8 +3,6 @@ ** ** GENERATED AUTOMATICALLY - DO NOT EDIT ** -** Creation date: Mon Mar 31 11:38:04 2025 -** ** This header file contains C Preprocessor macros which expand ** into the byte offsets needed to reach fields within structs ** used in the baseline system. Should those struct declarations diff --git a/kernel/kernel.c b/kernel/kernel.c index 44a8eee..ce2e9dd 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -163,7 +163,7 @@ static void kreport(bool_t dtrace) } #endif /* TRACE > 0 */ - cio_puts("\n-------------------------------\n"); + cio_putchar('\n'); } #if defined(CONSOLE_STATS) @@ -304,10 +304,10 @@ int main(void) user_init(); // user code handling cio_puts("\nModule initialization complete.\n"); - cio_puts("-------------------------------\n"); // report our configuration options kreport(true); + cio_puts("-------------------------------\n"); delay(DELAY_2_SEC); @@ -378,21 +378,23 @@ int main(void) sio_enable(SIO_RX); +#if 0 // produce a "system state" report - cio_puts("System status: Queues "); - pcb_queue_dump("R", ready, true); - pcb_queue_dump("W", waiting, true); - pcb_queue_dump("S", sleeping, true); - pcb_queue_dump("Z", zombie, true); - pcb_queue_dump("I", sioread, true); + cio_puts( "System status: Queues " ); + pcb_queue_dump( "R", ready, true ); + pcb_queue_dump( "W", waiting, true ); + pcb_queue_dump( "S", sleeping, true ); + pcb_queue_dump( "Z", zombie, true ); + pcb_queue_dump( "I", sioread, true ); ptable_dump_counts(); - pcb_dump("Current: ", current, true); + pcb_dump( "Current: ", current, true ); - delay(DELAY_3_SEC); + delay( DELAY_3_SEC ); - vm_print(current->pdir, true, TwoLevel); + vm_print( current->pdir, true, TwoLevel ); - delay(DELAY_3_SEC); + delay( DELAY_3_SEC ); +#endif return 0; } diff --git a/kernel/procs.c b/kernel/procs.c index 20e6784..82c4c98 100644 --- a/kernel/procs.c +++ b/kernel/procs.c @@ -963,16 +963,8 @@ void pcb_dump(const char *msg, register pcb_t *pcb, bool_t all) return; } - cio_printf(" %d", pcb->pid); - - cio_printf(" %s", pcb->state >= N_STATES ? "???" : state_str[pcb->state]); -#if 0 - if( pcb->state >= N_STATES ) { - cio_puts( " ????" ); - } else { - cio_printf( " %s", state_str[pcb->state] ); - } -#endif + cio_printf(" %d %s", pcb->pid, + pcb->state >= N_STATES ? "???" : state_str[pcb->state]); if (!all) { // just printing IDs and states on one line @@ -982,13 +974,6 @@ void pcb_dump(const char *msg, register pcb_t *pcb, bool_t all) // now, the rest of the contents cio_printf(" %s", pcb->priority >= N_PRIOS ? "???" : prio_str[pcb->priority]); -#if 0 - if( pcb->priority >= N_PRIOS ) { - cio_puts( " ???" ); - } else { - cio_printf( " %s", prio_str[pcb->priority] ); - } -#endif cio_printf(" ticks %u xit %d wake %08x\n", pcb->ticks, pcb->exit_status, pcb->wakeup); @@ -1007,8 +992,6 @@ void pcb_dump(const char *msg, register pcb_t *pcb, bool_t all) /** ** pcb_queue_dump(msg,queue,contents) ** -** Dump the contents of the specified queue to the console -** ** @param msg[in] Optional message to print ** @param queue[in] The queue to dump ** @param contents[in] Also dump (some) contents? @@ -1124,16 +1107,10 @@ void ptable_dump_counts(void) cio_printf("Ptable: %u ***", unknown); for (n = 0; n < N_STATES; ++n) { - cio_printf(" %u %s", nstate[n], - state_str[n] != NULL ? state_str[n] : "???"); -#if 0 - cio_printf( " %u ", nstate[n] ); - if( state_str[n][0] != '\0' ) { - cio_puts( state_str[n] ); - } else { - cio_puts( "???" ); + if (nstate[n]) { + cio_printf(" %u %s", nstate[n], + state_str[n] != NULL ? state_str[n] : "???"); } -#endif } cio_putchar('\n'); } diff --git a/kernel/user.c b/kernel/user.c index 0e5e186..5759534 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -510,45 +510,72 @@ static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, ** 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.........] + ** | ^ | | ^ ^ + ** | | | | | | + ** ------ | ---------------------|------- + ** --------------------------- */ /* - ** Find the user stack. The PDE entry for user address space points - ** to a page table for the first 4MB of the address space, but the - ** "pointer" there a physical frame address. + ** 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 address of the frame for the last one. (Again, we need - ** to convert it to a virtual address we can use.) + ** 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)); - // kernel VA for the first byte following that page - uint8_t *kv_ptr = (uint8_t *)P2V(PTE_ADDR(pmt_entry) + SZ_PAGE); - assert(kv_ptr != NULL); + // user VA for the first byte of that page + uint32_t *uvptr = (uint32_t *)USER_STACK_P2; - // user VA for the first byte following that page - uint32_t *uv_ptr = (uint32_t *)(USER_STACK_P2 + SZ_PAGE); - - // Pointers to where the arg strings should be filled in. - uint32_t kv_strings = ((uint32_t)kv_ptr) - argbytes; - uint32_t uv_strings = ((uint32_t)uv_ptr) - argbytes; - - // back the pointers up to the nearest word boundary; because we're - // moving toward location 0, the nearest word boundary is just the - // next smaller address whose low-order two bits are zeroes - kv_strings &= MOD4_MASK; - uv_strings &= MOD4_MASK; + // convert that address to a kernel VA + uint32_t *kvptr = (uint32_t *)vm_uva2kva(pcb->pdir, (void *)uvptr); /* - ** Next, we need to copy over the data. Start by determining where + ** 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, @@ -564,68 +591,37 @@ static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, ** 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 'strings'. + ** for argc and argv). We back up that much from the string area. */ int nwords = argc + 3; - uint32_t *kv_acptr = ((uint32_t *)kv_strings) - nwords; - uint32_t *uv_acptr = ((uint32_t *)uv_strings) - nwords; + 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 - kv_acptr = (uint32_t *)(((uint32_t)kv_acptr) & MOD16_MASK); - uv_acptr = (uint32_t *)(((uint32_t)uv_acptr) & MOD16_MASK); - - // the argv location - uint32_t *kv_avptr = kv_acptr + 1; - - // the user address for the first argv entry - uint32_t *uv_avptr = uv_acptr + 2; - - // Copy over the argv strings. - for (int i = 0; i < argc; ++i) { - // copy the string using kernel addresses - strcpy((char *)kv_strings, kv_args[i]); - - // remember the user address where this string went - uv_argv[i] = (char *)uv_strings; - - // adjust both string addresses - kv_strings += strlengths[i]; - uv_strings += strlengths[i]; - } - - /* - ** Next, we copy in argc, argv, and the pointers. The stack will - ** look something like this: - ** - ** kv_avptr - ** kv_acptr | - ** | | - ** v v - ** argc argv av[0] av[1] etc NULL str0 str1 etc. - ** [....][....][....][....] ... [0000] ... [......0......0.........] - ** | ^ | | ^ ^ - ** | | | | | | - ** ------ | ---------------------|------- - ** --------------------------- - */ + kvacptr = (uint32_t *)(((uint32_t)kvacptr) & MOD16_MASK); + uvacptr = (uint32_t *)(((uint32_t)uvacptr) & MOD16_MASK); // copy in 'argc' - *kv_acptr = argc; + *kvacptr = argc; - // copy in 'argv' - *kv_avptr++ = (uint32_t)uv_avptr; + // '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) { - *kv_avptr++ = (uint32_t)uv_argv[i]; + *kvavptr++ = (uint32_t)uv_argv[i]; } // and the trailing NULL - *kv_avptr = NULL; + *kvavptr = NULL; /* ** Almost done! @@ -641,8 +637,8 @@ static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, // Locate the context save area on the stack by backup up one // "context" from where the argc value is saved - context_t *kv_ctx = ((context_t *)kv_acptr) - 1; - uint32_t uv_ctx = (uint32_t)(((context_t *)uv_acptr) - 1); + 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 @@ -655,18 +651,18 @@ static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, ** where it winds up. */ - kv_ctx->eflags = DEFAULT_EFLAGS; // IF enabled, IOPL 0 - kv_ctx->eip = entry; // initial EIP - kv_ctx->cs = GDT_CODE; // segment registers - kv_ctx->ss = GDT_STACK; - kv_ctx->ds = kv_ctx->es = kv_ctx->fs = kv_ctx->gs = GDT_DATA; + 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. It will be our - ** caller's responsibility to schedule this process. + ** Return the new context pointer to the caller as a user + ** space virtual address. */ - return ((context_t *)uv_ctx); + return ((context_t *)uvctx); } /* diff --git a/kernel/vm.c b/kernel/vm.c index a700bcb..814ff12 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -95,13 +95,19 @@ static uint32_t ptcount(pte_t *ptr, bool_t dir) } // decode a PDE -static void pde_prt(uint32_t level, uint32_t i, uint32_t entry) +static void pde_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) { + if (!IS_PRESENT(entry) && !all) { + return; + } + // indent for (int n = 0; n <= level; ++n) cio_puts(" "); + // line header - cio_printf("[%08x] %08x", i, entry); + cio_printf("[%03x] %08x", i, entry); + // perms if (IS_LARGE(entry)) { // PS is 1 if ((entry & PDE_PAT) != 0) @@ -122,43 +128,49 @@ static void pde_prt(uint32_t level, uint32_t i, uint32_t entry) cio_puts(" U"); if ((entry & PDE_RW) != 0) cio_puts(" W"); - cio_puts((entry & PDE_P) != 0 ? " P" : "!P"); - cio_printf(" --> %s %08x", IS_LARGE(entry) ? "Pg" : "PT", PDE_ADDR(entry)); + // frame address + cio_printf(" P --> %s %08x\n", IS_LARGE(entry) ? "Pg" : "PT", + PDE_ADDR(entry)); } // decode a PTE -static void pte_prt(uint32_t level, uint32_t i, uint32_t entry) +static void pte_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) { + if (!IS_PRESENT(entry) && !all) { + return; + } + // indent for (int n = 0; n <= level; ++n) cio_puts(" "); - // line header - cio_printf("[%08x] %08x", i, entry); - // perms - if ((entry & PDE_G) != 0) - cio_puts(" G"); - if ((entry & PDE_PAT) != 0) - cio_puts(" PAT"); - if ((entry & PDE_D) != 0) - cio_puts(" D"); - if ((entry & PDE_A) != 0) - cio_puts(" A"); - if ((entry & PDE_PCD) != 0) - cio_puts(" CD"); - if ((entry & PDE_PWT) != 0) - cio_puts(" WT"); - if ((entry & PDE_US) != 0) - cio_puts(" U"); - if ((entry & PDE_RW) != 0) - cio_puts(" W"); - cio_puts((entry & PDE_P) != 0 ? " P" : "!P"); - cio_printf(" --> Pg %08x", PTE_ADDR(entry)); + // line header + cio_printf("[%03x] %08x", i, entry); + + // perms + if ((entry & PTE_G) != 0) + cio_puts(" G"); + if ((entry & PTE_PAT) != 0) + cio_puts(" PAT"); + if ((entry & PTE_D) != 0) + cio_puts(" D"); + if ((entry & PTE_A) != 0) + cio_puts(" A"); + if ((entry & PTE_PCD) != 0) + cio_puts(" CD"); + if ((entry & PTE_PWT) != 0) + cio_puts(" WT"); + if ((entry & PTE_US) != 0) + cio_puts(" U"); + if ((entry & PTE_RW) != 0) + cio_puts(" W"); + + cio_printf(" P --> Pg %08x\n", PTE_ADDR(entry)); } /** -** Name: pdump +** Name: ptdump ** ** Recursive helper for table hierarchy dump. ** @@ -168,40 +180,44 @@ static void pte_prt(uint32_t level, uint32_t i, uint32_t entry) ** @param mode How to display the entries */ ATTR_UNUSED -static void pdump(uint_t level, void *pt, bool_t dir, enum vmmode_e mode) +static void ptdump(uint_t level, void *pt, bool_t dir, enum vmmode_e mode) { pte_t *ptr = (pte_t *)pt; - cio_printf("? at 0x%08x:", dir ? "PDir" : "PTbl", (uint32_t)pt); + // indent two spaces per level + for (int n = 0; n < level; ++n) + cio_puts(" "); + + cio_printf("%s at 0x%08x:", dir ? "PDir" : "PTbl", (uint32_t)pt); uint32_t nums = ptcount(ptr, dir); if (dir) { - cio_printf(" %u 4MB", (nums >> 16)); + cio_printf(" 4MB=%u", (nums >> 16)); } - cio_printf(" %u P %u !P\n", nums & 0xffff, + cio_printf(" P=%u !P=%u\n", nums & 0xffff, N_PTE - ((nums >> 16) + (nums & 0xffff))); for (uint32_t i = 0; i < (uint32_t)N_PTE; ++i) { - pte_t entry = *ptr; - if (dir) { - // this is a PDIR entry; could be either a 4MB - // page, or a PMT pointer - if (mode > Simple) { - pde_prt(level, i, entry); - cio_putchar('\n'); - if (!IS_LARGE(entry)) { - pdump(level + 1, (void *)*ptr, false, mode); + pte_t entry = *ptr++; + + // only process this entry if it's not empty + if (entry) { + if (dir) { + // this is a PDIR entry; could be either a 4MB + // page, or a PMT pointer + if (mode > Simple) { + pde_prt(level, i, entry, false); + if (!IS_LARGE(entry) && mode > OneLevel) { + ptdump(level + 1, (void *)P2V(PTE_ADDR(entry)), false, + mode); + } + } + } else { + // just a PMT entry + if (mode > Simple) { + pte_prt(level, i, entry, false); } } - } else { - // just a PMT entry - if (mode > Simple) { - pte_prt(level, i, entry); - cio_putchar('\n'); - } } - - // move to the next entry - ++ptr; } } @@ -285,7 +301,7 @@ void vm_init(void) // add the entries for the user address space for (uint32_t addr = 0; addr < NUM_4MB; addr += SZ_PAGE) { - int stat = vm_map(kpdir, (void *)addr, addr, SZ_PAGE, PTE_RW); + int stat = vm_map(kpdir, (void *)addr, addr, SZ_PAGE, PTE_US | PTE_RW); if (stat != SUCCESS) { cio_printf("vm_init, map %08x->%08x failed, status %d\n", addr, addr, stat); @@ -315,8 +331,8 @@ void vm_init(void) ** ** Convert a user VA into a kernel address. Works for all addresses - ** if the address is a page address, the low-order nine bits will be -** zeroes; otherwise, they is the offset into the page, which is -** unchanged within the address spaces. +** zeroes; otherwise, they are the offset into the page, which is +** unchanged between the address spaces. ** ** @param pdir Pointer to the page directory to examine ** @param va Virtual address to check @@ -333,7 +349,7 @@ void *vm_uva2kva(pde_t *pdir, void *va) pte_t entry = *pte; // is this a valid address for the user? - if (IS_PRESENT(entry)) { + if (!IS_PRESENT(entry)) { return NULL; } @@ -477,7 +493,7 @@ pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc) // NOTE: the allocator is serving us virtual page addresses, // so we must convert them to physical addresses for the // table entries - *pde_ptr = V2P(ptbl) | PDE_P | PDE_RW; + *pde_ptr = V2P(ptbl) | PDE_P | PDE_RW | PDE_US; } // finally, return a pointer to the entry in the page table for this VA @@ -630,7 +646,7 @@ int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size, if (wr) { entrybase |= PTE_RW; } - if (sys) { + if (!sys) { entrybase |= PTE_US; } @@ -664,7 +680,7 @@ int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size, memclr(page, SZ_PAGE); // create the PTE for this frame - uint32_t entry = (uint32_t)(PTE_ADDR(V2P(page)) | entrybase); + uint32_t entry = (uint32_t)(V2P(PTE_ADDR(page)) | entrybase); *pte = entry; // copy data if we need to @@ -711,6 +727,7 @@ void vm_free(pde_t *pdir) pde_t *curr = pdir; int nf = 0; int nt = 0; + for (int i = 0; i < N_PDE; ++i) { // the entry itself pde_t entry = *curr; @@ -721,14 +738,15 @@ void vm_free(pde_t *pdir) assert(!IS_LARGE(entry)); // get the PMT pointer - pte_t *pmt = (pte_t *)PTE_ADDR(entry); + pte_t *pmt = (pte_t *)P2V(PTE_ADDR(entry)); // walk the PMT for (int j = 0; j < N_PTE; ++j) { + pte_t tmp = *pmt; // does this entry point to a frame? - if (IS_PRESENT(*pmt)) { + if (IS_PRESENT(tmp)) { // yes - free the frame - km_page_free((void *)PTE_ADDR(*pmt)); + km_page_free((void *)P2V(PTE_ADDR(tmp))); ++nf; // mark it so we don't get surprised *pmt = 0; @@ -737,7 +755,7 @@ void vm_free(pde_t *pdir) ++pmt; } // now, free the PMT itself - km_page_free((void *)PDE_ADDR(entry)); + km_page_free((void *)P2V(PDE_ADDR(entry))); ++nt; *curr = 0; } @@ -875,7 +893,8 @@ int vm_uvmdup(pde_t *new, pde_t *old) if (!IS_LARGE(entry)) { // it's a 4KB page, so we need to duplicate the PMT - pte_t *newpt = (pte_t *)vm_pagedup((void *)PTE_ADDR(entry)); + pte_t *newpt = + (pte_t *)vm_pagedup((void *)P2V(PTE_ADDR(entry))); if (newpt == NULL) { return E_NO_MEMORY; } @@ -883,7 +902,7 @@ int vm_uvmdup(pde_t *new, pde_t *old) uint32_t perms = PERMS(entry); // create the new PDE entry by replacing the frame # - entry = ((uint32_t)newpt) | perms; + entry = ((uint32_t)V2P(PTE_ADDR(newpt))) | perms; } } else { @@ -919,8 +938,8 @@ void vm_print(void *pt, bool_t dir, enum vmmode_e mode) return; } - cio_printf("Starting at 0x%08x (%s):\n", (uint32_t)pt, + cio_printf(", starting at 0x%08x (%s):\n", (uint32_t)pt, dir ? "PDIR" : "PMT"); - pdump(0, pt, dir, mode); + ptdump(1, pt, dir, mode); }