1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * Debugging functionality unique to 64-bit AMD processors.
  30  */
  31 
  32 #include <kmdb/kvm_cpu_impl.h>
  33 #include <kmdb/kmdb_dpi.h>
  34 #include <kmdb/kmdb_kdi.h>
  35 #include <kmdb/kvm.h>
  36 #include <mdb/mdb_err.h>
  37 #include <mdb/mdb.h>
  38 
  39 #include <sys/x86_archext.h>
  40 
  41 typedef struct kmt_cpu_amd {
  42         uint64_t amd_debugctl;          /* value for debugctl MSR */
  43         const kdi_msr_t *amd_msrs;      /* MSR r/w list */
  44         uint_t amd_family;              /* CPUID family */
  45         uint_t amd_model;               /* CPUID model */
  46 } kmt_cpu_amd_t;
  47 
  48 /*
  49  * The debugctl value in this struct needs to outlive the destruction of the
  50  * kmt_cpu_t.  It needs to be around for the final exit from the debugger so
  51  * we can do the final write of the debugctl MSR.
  52  */
  53 static kmt_cpu_amd_t kmt_cpu_amd;
  54 
  55 static void
  56 kmt_amd_branch(uint_t cpuid, const char *label, uint_t msr)
  57 {
  58         char buf[BUFSIZ];
  59         uintptr_t addr;
  60 
  61         addr = (uintptr_t)kmdb_dpi_msr_get_by_cpu(cpuid, msr);
  62 
  63         mdb_printf("%s: %p %A\n", label, addr, addr);
  64 
  65         if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target,
  66             MDB_TGT_AS_VIRT, buf, sizeof (buf), addr) != addr)
  67                 mdb_printf("%*s  %s\n", strlen(label), "", buf);
  68 }
  69 
  70 /*
  71  * MSRs for AMD processors with simple branch tracing facilities.  We'll use
  72  * this array if we can access listed LBR/LEX MSRs.
  73  */
  74 static const kdi_msr_t kmt_amd_msrs[] = {
  75         { MSR_DEBUGCTL, KDI_MSR_CLEARENTRY },
  76         { MSR_DEBUGCTL, KDI_MSR_WRITEDELAY, &kmt_cpu_amd.amd_debugctl },
  77         { MSR_LBR_TO,   KDI_MSR_READ },
  78         { MSR_LBR_FROM, KDI_MSR_READ },
  79         { MSR_LEX_TO,   KDI_MSR_READ },
  80         { MSR_LEX_FROM, KDI_MSR_READ },
  81         { NULL }
  82 };
  83 
  84 /*
  85  * Fallback MSR list for use if we can't read the LBR/LEX MSRs.
  86  */
  87 static const kdi_msr_t kmt_amdunk_msrs[] = {
  88         { MSR_DEBUGCTL, KDI_MSR_CLEARENTRY },
  89         { MSR_DEBUGCTL, KDI_MSR_WRITEDELAY, &kmt_cpu_amd.amd_debugctl },
  90         { NULL }
  91 };
  92 
  93 /*ARGSUSED*/
  94 static void
  95 kmt_amd_destroy(kmt_cpu_t *cpu)
  96 {
  97         /* Leave LBR on */
  98 
  99         mdb_free(cpu, sizeof (kmt_cpu_t));
 100 }
 101 
 102 /*ARGSUSED*/
 103 static const char *
 104 kmt_amd_name(kmt_cpu_t *cpu)
 105 {
 106         return ("AMD");
 107 }
 108 
 109 /*ARGSUSED*/
 110 static void
 111 kmt_amd_btf_clear(mdb_tgt_t *t, int id, void *arg)
 112 {
 113         kmt_cpu_amd_t *amd = arg;
 114         kreg_t efl;
 115 
 116         amd->amd_debugctl &= ~DEBUGCTL_BTF;
 117 
 118         (void) kmdb_dpi_get_register("rflags", &efl);
 119         efl &= ~(1 << KREG_EFLAGS_TF_SHIFT);
 120         (void) kmdb_dpi_set_register("rflags", efl);
 121 }
 122 
 123 /* Enable branch stepping, to be disabled on the next debugger entry */
 124 static int
 125 kmt_amd_step_branch(kmt_cpu_t *cpu, mdb_tgt_t *t)
 126 {
 127         kmt_cpu_amd_t *amd = cpu->kmt_cpu_data;
 128         kreg_t efl;
 129 
 130         (void) kmdb_dpi_get_register("rflags", &efl);
 131         (void) kmdb_dpi_set_register("rflags",
 132             (efl | (1 << KREG_EFLAGS_TF_SHIFT)));
 133 
 134         amd->amd_debugctl |= DEBUGCTL_BTF;
 135 
 136         return (mdb_tgt_add_fault(t, KMT_TRAP_ALL,
 137             MDB_TGT_SPEC_HIDDEN | MDB_TGT_SPEC_TEMPORARY,
 138             kmt_amd_btf_clear, amd));
 139 }
 140 
 141 static kmt_cpu_ops_t kmt_amd_ops = {
 142         kmt_amd_destroy,
 143         kmt_amd_name,
 144         kmt_amd_step_branch
 145 };
 146 
 147 /*ARGSUSED*/
 148 static int
 149 kmt_amd_branches(uintptr_t addr, uint_t flags, int argc,
 150     const mdb_arg_t *argv)
 151 {
 152         intptr_t cpuid = DPI_MASTER_CPUID;
 153 
 154         if (kmt_cpu_amd.amd_msrs == kmt_amdunk_msrs) {
 155                 warn("branch tracing unavailable on unknown AMD CPU "
 156                     "(id: %x/%x)\n", kmt_cpu_amd.amd_family,
 157                     kmt_cpu_amd.amd_model);
 158                 return (DCMD_ERR);
 159         }
 160 
 161         if (mdb_getopts(argc, argv,
 162             'c', MDB_OPT_UINTPTR, &cpuid,
 163             NULL) != argc)
 164                 return (DCMD_USAGE);
 165 
 166         kmt_amd_branch(cpuid, "LastBranchToIP     ", MSR_LBR_TO);
 167         kmt_amd_branch(cpuid, "LastBranchFromIP   ", MSR_LBR_FROM);
 168         kmt_amd_branch(cpuid, "LastExceptionToIP  ", MSR_LEX_TO);
 169         kmt_amd_branch(cpuid, "LastExceptionFromIP", MSR_LEX_FROM);
 170 
 171         return (0);
 172 }
 173 
 174 static const mdb_dcmd_t kmt_amd_dcmds[] = {
 175         { "branches", NULL, "describe the recently-taken branches",
 176             kmt_amd_branches },
 177         { NULL }
 178 };
 179 
 180 kmt_cpu_t *
 181 kmt_cpu_amd_create(mdb_tgt_t *t)
 182 {
 183         uint_t vendor, family, model;
 184         kmt_cpu_t *cpu;
 185 
 186         if (kmdb_kdi_get_cpuinfo(&vendor, &family, &model) < 0)
 187                 return (NULL); /* errno is set for us */
 188 
 189         if (vendor != X86_VENDOR_AMD) {
 190                 (void) set_errno(ENOTSUP);
 191                 return (NULL);
 192         }
 193 
 194         kmt_cpu_amd.amd_family = family;
 195         kmt_cpu_amd.amd_model = model;
 196         kmt_cpu_amd.amd_msrs = kmt_amdunk_msrs;
 197         kmt_cpu_amd.amd_debugctl = DEBUGCTL_LBR; /* Enable LBR on resume */
 198 
 199         cpu = mdb_zalloc(sizeof (kmt_cpu_t), UM_SLEEP);
 200         cpu->kmt_cpu_ops = &kmt_amd_ops;
 201         cpu->kmt_cpu_data = &kmt_cpu_amd;
 202 
 203         /*
 204          * Use the LBR/LEX MSRs if this CPU supports them.
 205          */
 206         if (kmt_msr_validate(kmt_amd_msrs))
 207                 kmt_cpu_amd.amd_msrs = kmt_amd_msrs;
 208 
 209         (void) mdb_tgt_register_dcmds(t, kmt_amd_dcmds, MDB_MOD_FORCE);
 210         kmdb_dpi_msr_add(kmt_cpu_amd.amd_msrs);
 211 
 212         return (cpu);
 213 }