// Simple command-line kernel monitor useful for // controlling the kernel and exploring the system interactively. #include #include #include #include #include #include #include #include #include #include #include #include #include #define CMDBUF_SIZE 80 // enough for one VGA text line struct Command { const char *name; const char *desc; // return -1 to force monitor to exit int (*func)(int argc, char** argv, struct Trapframe* tf); }; // LAB 1: add your command to here... static struct Command commands[] = { { "help", "Display this list of commands", mon_help }, { "kerninfo", "Display information about the kernel", mon_kerninfo }, { "backtrace", "Display current backtrace", mon_backtrace }, { "showmappings", "Display the physical mappings for range", mon_showmappings }, { "mperms", "Change the permissions of a memory range", mon_mperms }, { "resume", "Resume from a breakpoint", mon_resume }, { "step", "Step to next instruction", mon_step } }; /***** Implementations of basic kernel monitor commands *****/ int mon_help(int argc, char **argv, struct Trapframe *tf) { int i; for (i = 0; i < ARRAY_SIZE(commands); i++) cprintf("%s - %s\n", commands[i].name, commands[i].desc); return 0; } int mon_kerninfo(int argc, char **argv, struct Trapframe *tf) { extern char _start[], entry[], etext[], edata[], end[]; cprintf("Special kernel symbols:\n"); cprintf(" _start %08x (phys)\n", _start); cprintf(" entry %08x (virt) %08x (phys)\n", entry, entry - KERNBASE); cprintf(" etext %08x (virt) %08x (phys)\n", etext, etext - KERNBASE); cprintf(" edata %08x (virt) %08x (phys)\n", edata, edata - KERNBASE); cprintf(" end %08x (virt) %08x (phys)\n", end, end - KERNBASE); cprintf("Kernel executable memory footprint: %dKB\n", ROUNDUP(end - entry, 1024) / 1024); return 0; } int mon_backtrace(int argc, char **argv, struct Trapframe *tf) { // LAB 1: Your code here. // HINT 1: use read_ebp(). // HINT 2: print the current ebp on the first line (not current_ebp[0]) uint32_t* ebp; uint32_t eip; struct Eipdebuginfo info; ebp = (uint32_t*) read_ebp(); cprintf("Stack backtrace:\n"); while(ebp) { eip = ebp[1]; cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n", ebp, eip, ebp[2], ebp[3], ebp[4], ebp[5], ebp[6]); ebp = (uint32_t*) ebp[0]; debuginfo_eip(eip, &info); cprintf(" %s:%d: %.*s+%d\n", info.eip_file, info.eip_line, info.eip_fn_namelen, info.eip_fn_name, eip - info.eip_fn_addr); }; return 0; } #define EXPECT_ARGS(n, ac) if(ac - 1 != n) { \ cprintf(ACOL_ERR("Expected %d arguments, " \ "got %d\n"), n, ac - 1); \ return 1; } #define VA_TXT ACOL_CYAN "VA" ACOL_CLEAR #define PA_TXT ACOL_GREEN "PA" ACOL_CLEAR char* decode_pte_perms(pte_t pte, char* buffer) { buffer[0] = (pte & PTE_W) ? 'w' : 'r'; buffer[1] = (pte & PTE_U) ? 'u' : 'k'; return buffer; } void get_pagebounds(char* one, char* two, uintptr_t* pone, uintptr_t* ptwo) { long from = strtol(one, NULL, 0); long to = strtol(two, NULL, 0); *pone = ROUNDDOWN(from, PGSIZE); *ptwo = ROUNDUP(to, PGSIZE); if(*pone != from) cprintf(ACOL_WARN("Aligning start address %p down to %p\n"), from, *pone); if(*ptwo != to) cprintf(ACOL_WARN("Aligning end address %p up to %p\n"), to, *ptwo); } int mon_showmappings(int argc, char** argv, struct Trapframe* tf) { EXPECT_ARGS(2, argc); uintptr_t va_start, va_end; char buffer[3] = {0}; get_pagebounds(argv[1], argv[2], &va_start, &va_end); if(va_start == va_end) va_end += PGSIZE; while(va_start < va_end) { pte_t* pte = pgdir_walk(kern_pgdir, (const void*) va_start, 0); cprintf(VA_TXT " 0x%08p -> ", va_start); if(pte && (*pte & PTE_P)) { cprintf(PA_TXT " 0x%08p (%s)\n", PTE_ADDR(*pte), decode_pte_perms(*pte, buffer)); } else { cprintf(PA_TXT " ------------\n"); } va_start += PGSIZE; } return 0; } int mon_mperms(int argc, char** argv, struct Trapframe* tf) { EXPECT_ARGS(3, argc); pte_t perms = 0; enum { PERM_ADD, PERM_REMOVE, PERM_SET } pmode; const char* str = argv[1]; if(str[0] == '+') { pmode = PERM_ADD; str++; } else if(str[0] == '-') { pmode = PERM_REMOVE; str++; } else pmode = PERM_SET; while(*str) { if(*str == 'w') perms |= PTE_W; else if(*str == 'u') perms |= PTE_U; else { cprintf(ACOL_ERR("Unknown permission character %c\n"), *str); return 1; } str++; } uintptr_t va_start, va_end; get_pagebounds(argv[2], argv[3], &va_start, &va_end); if(va_start == va_end) va_end += PGSIZE; while(va_start < va_end) { pte_t* pte = pgdir_walk(kern_pgdir, (void*) va_start, 0); if(!pte || !(*pte & PTE_P)) { va_start += PGSIZE; continue; } if(pmode == PERM_ADD) { *pte |= perms; } else if(pmode == PERM_REMOVE) { *pte &= ~perms; } else if(pmode == PERM_SET) { *pte = PTE_ADDR(*pte) | perms | PTE_P; } va_start += PGSIZE; } return 0; } // This is a nice thought and all... // But we should modify the trap frame. // I feel stupid. static inline void set_eflag(uint16_t flagno, int value) { uint32_t temp; uint32_t mask = ~(1 << flagno); uint32_t regv = value << flagno; asm volatile("pushfl\n\t" "pop %w0\n\t" "and %w1, %w0\n\t" "or %w2, %w0\n\t" "push %w0\n\t" "popfl\n\t" : "=r" (temp) : "r" (mask), "r" (regv)); } #define EXPECT_BRKPT if(!(tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG)) { \ cprintf(ACOL_ERR("I don't think I should resume from this.\n")); \ return 0; } #define EFLAGS_TF 0x8 int mon_resume(int argc, char** argv, struct Trapframe* tf) { EXPECT_BRKPT; tf->tf_eflags &= ~(1 << EFLAGS_TF); env_pop_tf(tf); return 0; } int mon_step(int argc, char** argv, struct Trapframe* tf) { EXPECT_BRKPT; tf->tf_eflags |= 1 << EFLAGS_TF; env_pop_tf(tf); return 0; } /***** Kernel monitor command interpreter *****/ #define WHITESPACE "\t\r\n " #define MAXARGS 16 static int runcmd(char *buf, struct Trapframe *tf) { int argc; char *argv[MAXARGS]; int i; // Parse the command buffer into whitespace-separated arguments argc = 0; argv[argc] = 0; while (1) { // gobble whitespace while (*buf && strchr(WHITESPACE, *buf)) *buf++ = 0; if (*buf == 0) break; // save and scan past next arg if (argc == MAXARGS-1) { cprintf("Too many arguments (max %d)\n", MAXARGS); return 0; } argv[argc++] = buf; while (*buf && !strchr(WHITESPACE, *buf)) buf++; } argv[argc] = 0; // Lookup and invoke the command if (argc == 0) return 0; for (i = 0; i < ARRAY_SIZE(commands); i++) { if (strcmp(argv[0], commands[i].name) == 0) return commands[i].func(argc, argv, tf); } cprintf("Unknown command '%s'\n", argv[0]); return 0; } void monitor(struct Trapframe *tf) { char *buf; cprintf("Welcome to the JOS kernel monitor!\n"); cprintf("Type 'help' for a list of commands.\n"); if (tf != NULL) print_trapframe(tf); while (1) { buf = readline("K> "); if (buf != NULL) if (runcmd(buf, tf) < 0) break; } }