kdb.c (10796B)
1 /* 2 * core/kdb.c 3 * 4 * Copyright (C) 2010 stateless 5 */ 6 7 #include <common.h> 8 #include <kdb.h> 9 #include <tty.h> 10 #include <heap.h> 11 #include <string.h> 12 #include <errno.h> 13 #include <syscall.h> 14 #include <x86.h> 15 #include <pci.h> 16 #include <tss.h> 17 #include <stdlib.h> 18 #include <ctype.h> 19 #include <elf.h> 20 #include <rtl8139.h> 21 #include <pipe.h> 22 #include <rtc.h> 23 24 #define ENV_REPORT 25 26 enum { 27 CMD_SIZE = 512 28 }; 29 30 static void kdb_loop(void); 31 32 static int kdb_cmd_help(int argc, char **argv); 33 static int kdb_cmd_exit(int argc, char **argv); 34 static int kdb_cmd_reboot(int argc, char **argv); 35 static int kdb_cmd_lspci(int argc, char **argv); 36 static int kdb_cmd_ps(int argc, char **argv); 37 static int kdb_cmd_cpuid(int argc, char **argv); 38 static int kdb_cmd_heapdump(int argc, char **argv); 39 static int kdb_cmd_hexdump(int argc, char **argv); 40 static int kdb_cmd_write(int argc, char **argv); 41 static int kdb_cmd_symlookup(int argc, char **argv); 42 static int kdb_cmd_dumpregs(int argc, char **argv); 43 static int kdb_cmd_exec(int argc, char **argv); 44 static int kdb_cmd_ls(int argc, char **argv); 45 static int kdb_cmd_dumpframe(int argc, char **argv); 46 static int kdb_cmd_macaddr(int argc, char **argv); 47 static int kdb_cmd_test(int argc, char **argv); 48 static int kdb_cmd_dumpmap(int argc, char **argv); 49 50 static struct trapframe_t cframe; 51 static char *cmd; 52 53 static struct { 54 const char *name; 55 const char *desc; 56 int (*fnp)(int argc, char **argv); 57 } kdb_cmds[] = { 58 { "help", "Show this help menu.", kdb_cmd_help }, 59 { "exit", "Quit the debugger.", kdb_cmd_exit }, 60 { "reboot", "Reboot the machine.", kdb_cmd_reboot }, 61 { "lspci", "Perform a PCI enumeration.", kdb_cmd_lspci }, 62 { "ps", "Dump the scheduler's runqueue.", kdb_cmd_ps }, 63 { "cpuid", "Dump CPU info.", kdb_cmd_cpuid }, 64 { "heapdump", "Dump the kernel's heap allocation/deallocation lists.", kdb_cmd_heapdump }, 65 { "hexdump", "Do a hexdump of a specific memory range.", kdb_cmd_hexdump }, 66 { "write", "Write a specific value in a range of addresses.", kdb_cmd_write }, 67 { "symlookup", "Match an address to a symbol name.", kdb_cmd_symlookup }, 68 { "dumpregs", "Dump the system's registers.", kdb_cmd_dumpregs }, 69 { "exec", "Execute an ELF file.", kdb_cmd_exec }, 70 { "ls", "List directory contents.", kdb_cmd_ls }, 71 { "dumpframe", "Dump the last saved trapframe.", kdb_cmd_dumpframe }, 72 { "mac-addr", "Dump the mac-address.", kdb_cmd_macaddr }, 73 { "dumpmap", "Dump memory mappings.", kdb_cmd_dumpmap }, 74 { "test", "Testing routine.", kdb_cmd_test } 75 }; 76 77 void 78 set_trapframe(struct trapframe_t *tframe) 79 { 80 cframe = *tframe; 81 } 82 83 void 84 kdb_enter(void) 85 { 86 cmd = kmalloc(CMD_SIZE); 87 if (IS_ERR(cmd)) 88 return; 89 memset(cmd, 0, CMD_SIZE); 90 ccurr_col(BLACK, WHITE); 91 clear_scr(); 92 printf("type help for a list of commands.\n"); 93 kdb_loop(); 94 } 95 96 static char ** 97 parse_cmd(int *argc) 98 { 99 int f, i; 100 char *p, **argv, *tmp, *arg; 101 *argc = 0; 102 103 for (f = 1, p = cmd; *p; ++p) { 104 if (!f && *p == ' ') { 105 ++*argc; 106 f = 1; 107 continue; 108 } 109 if (f && *p != ' ') 110 f = 0; 111 } 112 if (!f) 113 ++*argc; 114 argv = kmalloc((*argc + 1) * sizeof(char *)); 115 if (IS_ERR(argv)) 116 panic("oom"); 117 for (p = cmd; *p; ++p, tmp = p) 118 ; /* nothing */ 119 for (p = cmd; *p; ++p) 120 if (*p == ' ') 121 *p = '\0'; 122 for (i = 0, f = 1, p = cmd; p < tmp; ++p) { 123 if (!f && *p == '\0') { 124 f = 1; 125 argv[i++] = arg; 126 continue; 127 } 128 if (f && *p) { 129 f = 0; 130 arg = p; 131 } 132 } 133 if (!f) 134 argv[i] = arg; 135 argv[*argc] = NULL; 136 return argv; 137 } 138 139 static char * 140 prepare_cmd(void) 141 { 142 char *p; 143 144 p = &cmd[CMD_SIZE - 1]; 145 while (p != cmd) { 146 if (*p == '\n') { 147 *p-- = '\0'; 148 continue; 149 } 150 --p; 151 } 152 while (*p != ' ') ++p; 153 *p = '\0'; 154 return p; 155 } 156 157 static void 158 trimstr(char *s) 159 { 160 char *ptr = s; 161 162 while (*ptr == ' ') ++ptr; 163 memmove(s, ptr, strlen(s) - (ptr - s)); 164 memset(s + strlen(s) - (ptr - s), 0, ptr - s); 165 ptr = s + strlen(s) - 1; 166 while (*ptr == ' ') --ptr; 167 if (*ptr) { 168 ++ptr; 169 *ptr = '\0'; 170 } 171 } 172 173 static void 174 kdb_loop(void) 175 { 176 size_t i; 177 char *p; 178 ssize_t r; 179 int argc, ret; 180 char **argv; 181 182 do { 183 again: 184 update_cursor(cursor_y, 2); 185 printf("> "); 186 r = sys_read(0, cmd, CMD_SIZE - 1); 187 cmd[r] = '\0'; 188 p = cmd; 189 trimstr(p); 190 for (; *p; ++p) 191 if (!isspace(*p)) 192 break; 193 if (*p == '\0') 194 goto again; 195 p = prepare_cmd(); 196 for (i = 0; i < 197 sizeof(kdb_cmds) / sizeof(kdb_cmds[0]); ++i) { 198 if (!strcmp(cmd, kdb_cmds[i].name)) { 199 *p = ' '; 200 argv = parse_cmd(&argc); 201 if (!kdb_cmds[i].fnp) 202 panic("%s: is not initialized properly!", 203 kdb_cmds[i].name); 204 ret = kdb_cmds[i].fnp(argc, argv); 205 #ifdef ENV_REPORT 206 if (ret < 0) { 207 printf("FAIL: %d", ret); 208 if (ret <= -1 && ret >= -44) 209 printf(":%s", sys_errlist[-ret - 1]); 210 putchar('\n'); 211 } 212 #endif 213 kfree(argv); 214 goto again; 215 } 216 } 217 p = cmd; 218 while (isspace(*p)) ++p; 219 printf("%s: command not found\n", p); 220 } while (1); 221 } 222 223 static int 224 kdb_cmd_help(int argc, char **argv) 225 { 226 size_t i; 227 228 if (argc != 1) { 229 printf("usage: %s\n", argv[0]); 230 return -EINVAL; 231 } 232 233 printf("Command list\n"); 234 for (i = 0; i < sizeof(kdb_cmds) / sizeof(kdb_cmds[0]); ++i) 235 printf(" %s -- %s\n", kdb_cmds[i].name, kdb_cmds[i].desc); 236 return 0; 237 } 238 239 static int 240 kdb_cmd_exit(int argc, char **argv) 241 { 242 if (argc != 1) { 243 printf("usage: %s\n", argv[0]); 244 return -EINVAL; 245 } 246 sys_exit(0); 247 return 0; 248 } 249 250 static int 251 kdb_cmd_reboot(int argc, char **argv) 252 { 253 if (argc != 1) { 254 printf("usage: %s\n", argv[0]); 255 return -EINVAL; 256 } 257 outb(0x64, 0xfe); 258 cli(); 259 hlt(); 260 return 0; 261 } 262 263 static int 264 kdb_cmd_lspci(int argc, char **argv) 265 { 266 if (argc != 1) { 267 printf("usage: %s\n", argv[0]); 268 return -EINVAL; 269 } 270 lspci(); 271 return 0; 272 } 273 274 static int 275 kdb_cmd_ps(int argc, char **argv) 276 { 277 if (argc != 1) { 278 printf("usage: %s\n", argv[0]); 279 return -EINVAL; 280 } 281 ps(); 282 return 0; 283 } 284 285 static int 286 kdb_cmd_cpuid(int argc, char **argv) 287 { 288 if (argc != 1) { 289 printf("usage: %s\n", argv[0]); 290 return -EINVAL; 291 } 292 do_cpuid(); 293 return 0; 294 } 295 296 static int 297 kdb_cmd_heapdump(int argc, char **argv) 298 { 299 if (argc != 1) { 300 printf("usage: %s\n", argv[0]); 301 return -EINVAL; 302 } 303 walk_heap_lists(); 304 return 0; 305 } 306 307 static int 308 kdb_cmd_hexdump(int argc, char **argv) 309 { 310 uint32_t addr, len; 311 312 if (argc != 3) { 313 printf("usage: %s memory-address len\n", argv[0]); 314 printf("NOTE: all arguments are specified in hex\n"); 315 return -EINVAL; 316 } 317 addr = strtoul(argv[1], NULL, 16); 318 len = strtoul(argv[2], NULL, 16); 319 hexdump((const void *)addr, len); 320 return 0; 321 } 322 323 static int 324 kdb_cmd_write(int argc, char **argv) 325 { 326 uint32_t val, low, high, *p; 327 328 if (argc != 4) { 329 printf("usage: %s value low-addr high-addr\n", argv[0]); 330 printf("NOTE: the range is [low-addr, high-addr), all arguments are specified in hex\n"); 331 return -EINVAL; 332 } 333 val = strtoul(argv[1], NULL, 16); 334 low = strtoul(argv[2], NULL, 16); 335 high = strtoul(argv[3], NULL, 16); 336 if (high <= low) { 337 printf("high-addr can't be lower or equal to low-addr\n"); 338 return -EINVAL; 339 } 340 p = (uint32_t *)low; 341 while (p < (uint32_t *)high) 342 *p++ = val; 343 return 0; 344 } 345 346 static int 347 kdb_cmd_symlookup(int argc, char **argv) 348 { 349 uint32_t addr; 350 const char *sym; 351 352 if (argc != 2) { 353 printf("usage: %s symbol-address\n", argv[0]); 354 printf("NOTE: the address must be specified in hex\n"); 355 return -EINVAL; 356 } 357 addr = strtoul(argv[1], NULL, 16); 358 sym = find_symbol(addr); 359 printf("%s => %s\n", argv[1], (!sym) ? "unknown" : sym); 360 return 0; 361 } 362 363 /* TODO: add more regs */ 364 static int 365 kdb_cmd_dumpregs(int argc, char **argv) 366 { 367 uint32_t eax, ebx, ecx, edx, esp, ebp; 368 uint32_t edi, esi; 369 370 if (argc != 1) { 371 printf("usage: %s\n", argv[0]); 372 return -EINVAL; 373 } 374 375 asm volatile ("movl %%eax, %0" : "=r"(eax)); 376 asm volatile ("movl %%ebx, %0" : "=r"(ebx)); 377 asm volatile ("movl %%ecx, %0" : "=r"(ecx)); 378 asm volatile ("movl %%edx, %0" : "=r"(edx)); 379 asm volatile ("movl %%esp, %0" : "=r"(esp)); 380 asm volatile ("movl %%ebp, %0" : "=r"(ebp)); 381 asm volatile ("movl %%esi, %0" : "=r"(esi)); 382 asm volatile ("movl %%edi, %0" : "=r"(edi)); 383 384 printf("eax = 0x%08lx\nebx = 0x%08lx\necx = 0x%08lx\n", 385 eax, ebx, ecx); 386 printf("edx = 0x%08lx\nesp = 0x%08lx\nebp = 0x%08lx\n", 387 edx, esp, ebp); 388 printf("esi = 0x%08lx\nedi = 0x%08lx\n", 389 esi, edi); 390 return 0; 391 } 392 393 static int 394 kdb_cmd_exec(int argc, char **argv) 395 { 396 pid_t pid; 397 int ret, status; 398 399 if (argc != 2) { 400 printf("usage: %s filename\n", argv[0]); 401 return -EINVAL; 402 } 403 404 pid = sys_fork(); 405 if (pid < 0) 406 return pid; 407 if (!pid) { 408 ret = sys_execve(argv[1], NULL, NULL); 409 sys_exit(ret); 410 } else { 411 while (1) { 412 if ((ret = sys_waitpid(-1, &status, WNOHANG)) < 0) 413 break; 414 } 415 } 416 printf("child exited with error code %d\n", status); 417 return 0; 418 } 419 420 static int 421 kdb_cmd_ls(int argc, char **argv) 422 { 423 char buf[256]; 424 DIR *dirp; 425 struct dirent_t *dirent; 426 struct stat stbuf; 427 int ret; 428 429 if (argc != 1) { 430 printf("usage: %s\n", argv[0]); 431 return -EINVAL; 432 } 433 434 dirp = opendir("/"); 435 if (!dirp) 436 return -errno; 437 dirent = sys_readdir(dirp); 438 printf("%-12s%-12s%-12s%-12s\n", "FILENAME", "TYPE", "UID", "GID"); 439 while (dirent) { 440 if (!strcmp(dirent->d_name, ".") 441 || !strcmp(dirent->d_name, "..")) 442 goto out; 443 bzero(buf, 256); 444 buf[0] = '/'; 445 strncpy(buf + 1, dirent->d_name, 255); 446 buf[255] = '\0'; 447 if (!stat(buf, &stbuf)) { 448 printf("%-12s%-12s%-12d%-12d\n", 449 dirent->d_name, 450 S_ISREG(stbuf.st_mode) ? "FILE" : "DIR", 451 stbuf.st_uid, 452 stbuf.st_gid); 453 } 454 out: 455 dirent = readdir(dirp); 456 } 457 ret = closedir(dirp); 458 if (ret < 0) 459 return -errno; 460 return 0; 461 } 462 463 static int 464 kdb_cmd_dumpframe(int argc, char **argv) 465 { 466 if (argc != 1) { 467 printf("usage: %s\n", argv[0]); 468 return -EINVAL; 469 } 470 DUMP_STACK_REGS(cframe); 471 return 0; 472 } 473 474 static int 475 kdb_cmd_macaddr(int argc, char **argv) 476 { 477 const uint8_t *mac_addr; 478 479 if (argc != 1) { 480 printf("usage: %s\n", argv[0]); 481 return -EINVAL; 482 } 483 mac_addr = get_mac_addr(); 484 if (!mac_addr) { 485 info("no mac-addr?\n"); 486 return -ENODEV; 487 } 488 printf("%02x:%02x:%02x:%02x:%02x:%02x\n", 489 mac_addr[0], 490 mac_addr[1], 491 mac_addr[2], 492 mac_addr[3], 493 mac_addr[4], 494 mac_addr[5] 495 ); 496 return 0; 497 } 498 499 static int 500 kdb_cmd_dumpmap(int argc, char **argv) 501 { 502 if (argc != 1) { 503 printf("usage: %s\n", argv[0]); 504 return -EINVAL; 505 } 506 dump_mappings(); 507 return 0; 508 } 509 510 static int 511 kdb_cmd_test(int argc, char **argv) 512 { 513 ssize_t r; 514 int pipefd[2]; 515 char buf[32]; 516 pid_t pid; 517 518 if (argc != 1) { 519 printf("usage: %s\n", argv[0]); 520 return -EINVAL; 521 } 522 523 bzero(buf, sizeof buf); 524 if (sys_pipe(pipefd) < 0) 525 panic("sys_pipe failed"); 526 pid = sys_fork(); 527 if (!pid) { 528 assert(!sys_close(pipefd[1])); 529 sleep(1); 530 r = sys_read(pipefd[0], buf, 13); 531 if (r < 0) 532 kerror(-r); 533 if (r) 534 printf("%s", buf); 535 sys_exit(0); 536 } 537 assert(!sys_close(pipefd[0])); 538 assert(sys_write(pipefd[1], "hello", 5) == 5); 539 assert(sys_write(pipefd[1], ", world\n", 8) == 8); 540 return 0; 541 } 542