/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __sparc #define KMDB_STACK_SIZE (384 * 1024) #else #define KMDB_STACK_SIZE (192 * 1024) #endif caddr_t kmdb_main_stack; size_t kmdb_main_stack_size; #define KMDB_DEF_IPATH "internal" #if defined(_LP64) #define KMDB_DEF_LPATH \ "%r/platform/%p/kernel/kmdb/%i:" \ "%r/platform/%m/kernel/kmdb/%i:" \ "%r/kernel/kmdb/%i" #else #define KMDB_DEF_LPATH \ "%r/platform/%m/kernel/kmdb:" \ "%r/kernel/kmdb" #endif #define MDB_DEF_PROMPT "[%<_cpuid>]> " #define KMDB_DEF_TERM_TYPE "vt100" /* * Similar to the panic_* variables in the kernel, we keep some relevant * information stored in a set of global _mdb_abort_* variables; in the * event that the debugger dumps core, these will aid core dump analysis. */ const char *volatile _mdb_abort_str; /* reason for failure */ /* * The kernel supplies a space-delimited list of directories * (/platform/sun4u/kernel /kernel /usr/kernel ...) which we must transform into * a debugger-friendly, colon-delimited module search path. We add the kmdb * module directory to each component and change the delimiter. */ static char * kmdb_modpath2lpath(const char *modpath) { #ifdef _LP64 static const char suffix[] = "/kmdb/%i:"; #else static const char suffix[] = "/kmdb:"; #endif const char *c; char *lpath, *lpend, *nlpath; size_t lpsz, lpres; if (strchr(modpath, ':') != NULL) { warn("invalid kernel module path\n"); return (NULL); } lpres = lpsz = strlen(modpath) + MAXPATHLEN; lpend = lpath = mdb_zalloc(lpsz, UM_SLEEP); while (isspace(*modpath)) modpath++; for (; *modpath != '\0'; modpath = c) { size_t sz; for (c = modpath; !isspace(*c) && *c != '\0'; c++) continue; sz = (c - modpath) + sizeof (suffix) - 1; if (sz >= lpres) continue; (void) strncpy(lpend, modpath, c - modpath); (void) strcpy(lpend + (c - modpath), suffix); lpend += sz; lpres -= sz; while (isspace(*c)) c++; } if (lpend != lpath) lpend[-1] = '\0'; /* eat trailing colon */ nlpath = strdup(lpath); mdb_free(lpath, lpsz); return (nlpath); } /* * called while the kernel is running */ int kmdb_init(const char *execname, kmdb_auxv_t *kav) { mdb_io_t *in_io, *out_io, *err_io, *null_io; mdb_tgt_ctor_f *tgt_ctor = kmdb_kvm_create; mdb_tgt_t *tgt; int i; /* * The beginnings of debugger initialization are a bit of a dance, due * to interlocking dependencies between kmdb_prom_init, * mdb_umem_startup, and mdb_create. In particular, allocator * initialization can't begin until prom_init() is called, * kmdb_prom_init can't finish until the allocator is ready and * mdb_create has been called. We therefore split kmdb_prom_init into * two pieces, and call thembefore and after umem initialization and * mdb_create. */ kmdb_prom_init_begin("kmdb", kav); mdb_umem_startup(kav->kav_dseg, kav->kav_dseg_size, kav->kav_pagesize); mdb_create(execname, "kmdb"); kmdb_prom_init_finish(kav); mdb.m_dseg = kav->kav_dseg; mdb.m_dsegsz = kav->kav_dseg_size; out_io = kmdb_promio_create("stdout"); mdb.m_out = mdb_iob_create(out_io, MDB_IOB_WRONLY); err_io = kmdb_promio_create("stderr"); mdb.m_err = mdb_iob_create(err_io, MDB_IOB_WRONLY); mdb_iob_clrflags(mdb.m_err, MDB_IOB_AUTOWRAP); null_io = mdb_nullio_create(); mdb.m_null = mdb_iob_create(null_io, MDB_IOB_WRONLY); in_io = kmdb_promio_create("stdin"); mdb.m_term = NULL; if (kav->kav_config != NULL) mdb_set_config(kav->kav_config); if (kav->kav_argv != NULL) { for (i = 0; kav->kav_argv[i] != NULL; i++) { if (!mdb_set_options(kav->kav_argv[i], TRUE)) return (-1); } } if (kav->kav_flags & KMDB_AUXV_FL_NOUNLOAD) mdb.m_flags |= MDB_FL_NOUNLOAD; mdb.m_in = mdb_iob_create(in_io, MDB_IOB_RDONLY); mdb_iob_setflags(mdb.m_in, MDB_IOB_TTYLIKE); mdb_lex_reset(); kmdb_kdi_init(kav->kav_kdi, kav); if (kmdb_dpi_init(kav) < 0) { warn("Couldn't initialize kernel/PROM interface\n"); return (-1); } /* * Path evaluation part 1: Create the initial module path to allow * the target constructor to load a support module. We base kmdb's * module path off the kernel's module path unless the user has * explicitly supplied one. */ mdb_set_ipath(KMDB_DEF_IPATH); if (strlen(mdb.m_lpathstr) > 0) { mdb_set_lpath(mdb.m_lpathstr); } else { char *lpath; if (kav->kav_modpath != NULL && *kav->kav_modpath != '\0' && (lpath = kmdb_modpath2lpath(kav->kav_modpath)) != NULL) { mdb_set_lpath(lpath); strfree(lpath); } else { mdb_set_lpath(KMDB_DEF_LPATH); } } if (mdb_get_prompt() == NULL) (void) mdb_set_prompt(MDB_DEF_PROMPT); tgt = mdb_tgt_create(tgt_ctor, mdb.m_tgtflags, 0, NULL); if (tgt == NULL) { warn("failed to initialize target"); return (-1); } mdb_tgt_activate(tgt); mdb_create_loadable_disasms(); /* * Path evaluation part 2: Re-evaluate the path now that the target * is ready (and thus we have access to the real platform string). */ mdb_set_ipath(mdb.m_ipathstr); mdb_set_lpath(mdb.m_lpathstr); if (!(mdb.m_flags & MDB_FL_NOMODS)) mdb_module_load_all(MDB_MOD_DEFER); /* Allocate the main debugger stack */ kmdb_main_stack = mdb_alloc(KMDB_STACK_SIZE, UM_SLEEP); kmdb_main_stack_size = KMDB_STACK_SIZE; kmdb_kdi_end_init(); return (0); } #ifdef sun4v void kmdb_init_promif(char *pgmname, kmdb_auxv_t *kav) { kmdb_prom_init_promif(pgmname, kav); } #else /*ARGSUSED*/ void kmdb_init_promif(char *pgmname, kmdb_auxv_t *kav) { /* * Fake function for non sun4v. See comments in kmdb_ctl.h */ ASSERT(0); } #endif /* * First-time kmdb startup. Run when kmdb has control of the machine for the * first time. */ static void kmdb_startup(void) { mdb_io_t *inio, *outio; if (mdb.m_termtype == NULL) { /* * The terminal type wasn't specified, so we guess. If we're * on console, we'll get a terminal type from the PROM. If not, * we'll use the default. */ const char *ttype; if ((ttype = kmdb_prom_term_type()) == NULL) { ttype = KMDB_DEF_TERM_TYPE; warn("unable to determine terminal type: " "assuming `%s'\n", ttype); } mdb.m_flags |= MDB_FL_TERMGUESS; mdb.m_termtype = strdup(ttype); } else if (mdb.m_flags & MDB_FL_TERMGUESS) { /* * The terminal type wasn't specified by the user, but a guess * was made using either $TERM or a property from the SMF. A * terminal type from the PROM code overrides the guess, so * we'll use that if we can. */ char *promttype; if ((promttype = kmdb_prom_term_type()) != NULL) { strfree(mdb.m_termtype); mdb.m_termtype = strdup(promttype); } } inio = kmdb_promio_create("stdin"); outio = kmdb_promio_create("stdout"); if ((mdb.m_term = mdb_termio_create(mdb.m_termtype, inio, outio)) == NULL && strcmp(mdb.m_termtype, KMDB_DEF_TERM_TYPE) != 0) { warn("failed to set terminal type to `%s', using `" KMDB_DEF_TERM_TYPE "'\n", mdb.m_termtype); strfree(mdb.m_termtype); mdb.m_termtype = strdup(KMDB_DEF_TERM_TYPE); if ((mdb.m_term = mdb_termio_create(mdb.m_termtype, inio, outio)) == NULL) { fail("failed to set terminal type to `" KMDB_DEF_TERM_TYPE "'\n"); } } mdb_iob_destroy(mdb.m_in); mdb.m_in = mdb_iob_create(mdb.m_term, MDB_IOB_RDONLY); mdb_iob_setpager(mdb.m_out, mdb.m_term); mdb_iob_setflags(mdb.m_out, MDB_IOB_PGENABLE); kmdb_kvm_startup(); /* * kmdb_init() and kctl_activate() may have been talking to each other, * and may have left some messages for us. The driver -> debugger * queue is normally processed during the resume path, so we have to * do it manually here if we want it to be run for first startup. */ kmdb_dpi_process_work_queue(); kmdb_kvm_poststartup(); } void kmdb_main(void) { int status; kmdb_dpi_set_state(DPI_STATE_STOPPED, 0); mdb_printf("\nWelcome to kmdb\n"); kmdb_startup(); /* * Debugger termination is a bit tricky. For compatibility with kadb, * neither an EOF on stdin nor a normal ::quit will cause the debugger * to unload. In both cases, they get a trip to OBP, after which the * debugger returns. * * The only supported way to cause the debugger to unload is to specify * the unload flag to ::quit, or to have the driver request it. The * driver request is the primary exit mechanism - the ::quit flag is * provided for convenience. * * Both forms of "exit" (unqualified ::quit that won't cause an unload, * and a driver request that will) are signalled by an MDB_ERR_QUIT. In * the latter case, however, the KDI will have the unload request. */ for (;;) { status = mdb_run(); if (status == MDB_ERR_QUIT && kmdb_kdi_get_unload_request()) { break; } else if (status == MDB_ERR_QUIT || status == 0) { kmdb_dpi_enter_mon(); } else if (status == MDB_ERR_OUTPUT) { /* * If a write failed on stdout, give up. A more * informative error message will already have been * printed by mdb_run(). */ if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_ERR) fail("write to stdout failed, exiting\n"); } else if (status != MDB_ERR_ABORT) { fail("debugger exited abnormally (status = %s)\n", mdb_err2str(status)); } } mdb_destroy(); kmdb_dpi_resume_unload(); /*NOTREACHED*/ }