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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2019 Doma Gergő Mihály <doma.gergo.mihaly@gmail.com> 28 * Copyright 2018 Joyent, Inc. 29 * Copyright 2023 Oxide Computer Company 30 */ 31 32 /* 33 * User Process Target Intel 32-bit component 34 * 35 * This file provides the ISA-dependent portion of the user process target. 36 * For more details on the implementation refer to mdb_proc.c. 37 */ 38 39 #include <mdb/mdb_proc.h> 40 #include <mdb/mdb_kreg.h> 41 #include <mdb/mdb_err.h> 42 #include <mdb/mdb_isautil.h> 43 #include <mdb/mdb_ia32util.h> 44 #include <mdb/proc_x86util.h> 45 #include <mdb/mdb.h> 46 47 #include <sys/ucontext.h> 48 #include <sys/frame.h> 49 #include <libproc.h> 50 #include <sys/fp.h> 51 #include <ieeefp.h> 52 #include <sys/sysmacros.h> 53 54 #include <stddef.h> 55 56 const mdb_tgt_regdesc_t pt_regdesc[] = { 57 { "gs", GS, MDB_TGT_R_EXPORT }, 58 { "fs", FS, MDB_TGT_R_EXPORT }, 59 { "es", ES, MDB_TGT_R_EXPORT }, 60 { "ds", DS, MDB_TGT_R_EXPORT }, 61 { "edi", EDI, MDB_TGT_R_EXPORT }, 62 { "di", EDI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 63 { "esi", ESI, MDB_TGT_R_EXPORT }, 64 { "si", ESI, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 65 { "ebp", EBP, MDB_TGT_R_EXPORT }, 66 { "bp", EBP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 67 { "kesp", ESP, MDB_TGT_R_EXPORT }, 68 { "ksp", ESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 69 { "ebx", EBX, MDB_TGT_R_EXPORT }, 70 { "bx", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 71 { "bh", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H }, 72 { "bl", EBX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L }, 73 { "edx", EDX, MDB_TGT_R_EXPORT }, 74 { "dx", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 75 { "dh", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H }, 76 { "dl", EDX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L }, 77 { "ecx", ECX, MDB_TGT_R_EXPORT }, 78 { "cx", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 79 { "ch", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H }, 80 { "cl", ECX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L }, 81 { "eax", EAX, MDB_TGT_R_EXPORT }, 82 { "ax", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 83 { "ah", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8H }, 84 { "al", EAX, MDB_TGT_R_EXPORT | MDB_TGT_R_8L }, 85 { "trapno", TRAPNO, MDB_TGT_R_EXPORT }, 86 { "err", ERR, MDB_TGT_R_EXPORT }, 87 { "eip", EIP, MDB_TGT_R_EXPORT }, 88 { "cs", CS, MDB_TGT_R_EXPORT }, 89 { "eflags", EFL, MDB_TGT_R_EXPORT }, 90 { "esp", UESP, MDB_TGT_R_EXPORT }, 91 { "sp", UESP, MDB_TGT_R_EXPORT | MDB_TGT_R_16 }, 92 { "ss", SS, MDB_TGT_R_EXPORT }, 93 { NULL, 0, 0 } 94 }; 95 96 /* 97 * We cannot rely on pr_instr, because if we hit a breakpoint or the user has 98 * artifically modified memory, it will no longer be correct. 99 */ 100 static uint8_t 101 pt_read_instr(mdb_tgt_t *t) 102 { 103 const lwpstatus_t *psp = &Pstatus(t->t_pshandle)->pr_lwp; 104 uint8_t ret = 0; 105 106 (void) mdb_tgt_aread(t, MDB_TGT_AS_VIRT_I, &ret, sizeof (ret), 107 psp->pr_reg[EIP]); 108 109 return (ret); 110 } 111 112 /*ARGSUSED*/ 113 int 114 pt_regs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 115 { 116 mdb_tgt_t *t = mdb.m_target; 117 mdb_tgt_tid_t tid; 118 prgregset_t grs; 119 prgreg_t eflags; 120 boolean_t from_ucontext = B_FALSE; 121 122 if (mdb_getopts(argc, argv, 123 'u', MDB_OPT_SETBITS, B_TRUE, &from_ucontext, NULL) != argc) { 124 return (DCMD_USAGE); 125 } 126 127 if (from_ucontext) { 128 int off; 129 int o0, o1; 130 131 if (!(flags & DCMD_ADDRSPEC)) { 132 mdb_warn("-u requires a ucontext_t address\n"); 133 return (DCMD_ERR); 134 } 135 136 o0 = mdb_ctf_offsetof_by_name("ucontext_t", "uc_mcontext"); 137 o1 = mdb_ctf_offsetof_by_name("mcontext_t", "gregs"); 138 if (o0 == -1 || o1 == -1) { 139 off = offsetof(ucontext_t, uc_mcontext) + 140 offsetof(mcontext_t, gregs); 141 } else { 142 off = o0 + o1; 143 } 144 145 if (mdb_vread(&grs, sizeof (grs), addr + off) != sizeof (grs)) { 146 mdb_warn("failed to read from ucontext_t %p", addr); 147 return (DCMD_ERR); 148 } 149 goto print_regs; 150 } 151 152 if (t->t_pshandle == NULL || Pstate(t->t_pshandle) == PS_UNDEAD) { 153 mdb_warn("no process active\n"); 154 return (DCMD_ERR); 155 } 156 157 if (Pstate(t->t_pshandle) == PS_LOST) { 158 mdb_warn("debugger has lost control of process\n"); 159 return (DCMD_ERR); 160 } 161 162 if (flags & DCMD_ADDRSPEC) 163 tid = (mdb_tgt_tid_t)addr; 164 else 165 tid = PTL_TID(t); 166 167 if (PTL_GETREGS(t, tid, grs) != 0) { 168 mdb_warn("failed to get current register set"); 169 return (DCMD_ERR); 170 } 171 172 print_regs: 173 eflags = grs[EFL]; 174 175 mdb_printf("%%cs = 0x%04x\t\t%%eax = 0x%0?p %A\n", 176 grs[CS], grs[EAX], grs[EAX]); 177 178 mdb_printf("%%ds = 0x%04x\t\t%%ebx = 0x%0?p %A\n", 179 grs[DS], grs[EBX], grs[EBX]); 180 181 mdb_printf("%%ss = 0x%04x\t\t%%ecx = 0x%0?p %A\n", 182 grs[SS], grs[ECX], grs[ECX]); 183 184 mdb_printf("%%es = 0x%04x\t\t%%edx = 0x%0?p %A\n", 185 grs[ES], grs[EDX], grs[EDX]); 186 187 mdb_printf("%%fs = 0x%04x\t\t%%esi = 0x%0?p %A\n", 188 grs[FS], grs[ESI], grs[ESI]); 189 190 mdb_printf("%%gs = 0x%04x\t\t%%edi = 0x%0?p %A\n\n", 191 grs[GS], grs[EDI], grs[EDI]); 192 193 mdb_printf(" %%eip = 0x%0?p %A\n", grs[EIP], grs[EIP]); 194 mdb_printf(" %%ebp = 0x%0?p\n", grs[EBP]); 195 mdb_printf("%%kesp = 0x%0?p\n\n", grs[ESP]); 196 mdb_printf("%%eflags = 0x%08x\n", eflags); 197 198 mdb_printf(" id=%u vip=%u vif=%u ac=%u vm=%u rf=%u nt=%u iopl=0x%x\n", 199 (eflags & KREG_EFLAGS_ID_MASK) >> KREG_EFLAGS_ID_SHIFT, 200 (eflags & KREG_EFLAGS_VIP_MASK) >> KREG_EFLAGS_VIP_SHIFT, 201 (eflags & KREG_EFLAGS_VIF_MASK) >> KREG_EFLAGS_VIF_SHIFT, 202 (eflags & KREG_EFLAGS_AC_MASK) >> KREG_EFLAGS_AC_SHIFT, 203 (eflags & KREG_EFLAGS_VM_MASK) >> KREG_EFLAGS_VM_SHIFT, 204 (eflags & KREG_EFLAGS_RF_MASK) >> KREG_EFLAGS_RF_SHIFT, 205 (eflags & KREG_EFLAGS_NT_MASK) >> KREG_EFLAGS_NT_SHIFT, 206 (eflags & KREG_EFLAGS_IOPL_MASK) >> KREG_EFLAGS_IOPL_SHIFT); 207 208 mdb_printf(" status=<%s,%s,%s,%s,%s,%s,%s,%s,%s>\n\n", 209 (eflags & KREG_EFLAGS_OF_MASK) ? "OF" : "of", 210 (eflags & KREG_EFLAGS_DF_MASK) ? "DF" : "df", 211 (eflags & KREG_EFLAGS_IF_MASK) ? "IF" : "if", 212 (eflags & KREG_EFLAGS_TF_MASK) ? "TF" : "tf", 213 (eflags & KREG_EFLAGS_SF_MASK) ? "SF" : "sf", 214 (eflags & KREG_EFLAGS_ZF_MASK) ? "ZF" : "zf", 215 (eflags & KREG_EFLAGS_AF_MASK) ? "AF" : "af", 216 (eflags & KREG_EFLAGS_PF_MASK) ? "PF" : "pf", 217 (eflags & KREG_EFLAGS_CF_MASK) ? "CF" : "cf"); 218 219 mdb_printf(" %%esp = 0x%0?x\n", grs[UESP]); 220 mdb_printf("%%trapno = 0x%x\n", grs[TRAPNO]); 221 mdb_printf(" %%err = 0x%x\n", grs[ERR]); 222 223 return (DCMD_OK); 224 } 225 226 int 227 pt_fpregs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 228 { 229 int ret; 230 prfpregset_t fprs; 231 struct _fpstate fps; 232 char buf[256]; 233 uint_t top; 234 size_t i; 235 236 /* 237 * Union for overlaying _fpreg structure on to quad-precision 238 * floating-point value (long double). 239 */ 240 union { 241 struct _fpreg reg; 242 long double ld; 243 } fpru; 244 245 /* 246 * We use common code between 32-bit and 64-bit x86 to capture and print 247 * the extended vector state. The remaining classic 387 state is 248 * finicky and different enough that it is left to be dealt with on its 249 * own. 250 */ 251 if ((ret = x86_pt_fpregs_common(addr, flags, argc, &fprs)) != DCMD_OK) 252 return (ret); 253 254 bcopy(&fprs.fp_reg_set.fpchip_state, &fps, sizeof (fps)); 255 mdb_printf("387 and FP Control State\n"); 256 257 fps.cw &= 0xffff; /* control word is really 16 bits */ 258 fps.sw &= 0xffff; /* status word is really 16 bits */ 259 fps.status &= 0xffff; /* saved status word is really 16 bits */ 260 fps.cssel &= 0xffff; /* %cs is really 16-bits */ 261 fps.datasel &= 0xffff; /* %ds is really 16-bits too */ 262 263 mdb_printf("cw 0x%04x (%s)\n", fps.cw, 264 fpcw2str(fps.cw, buf, sizeof (buf))); 265 266 top = (fps.sw & FPS_TOP) >> 11; 267 mdb_printf("sw 0x%04x (TOP=0t%u) (%s)\n", fps.sw, 268 top, fpsw2str(fps.sw, buf, sizeof (buf))); 269 270 mdb_printf("xcp sw 0x%04x (%s)\n\n", fps.status, 271 fpsw2str(fps.status, buf, sizeof (buf))); 272 273 mdb_printf("ipoff %a\n", fps.ipoff); 274 mdb_printf("cssel 0x%x\n", fps.cssel); 275 mdb_printf("dtoff %a\n", fps.dataoff); 276 mdb_printf("dtsel 0x%x\n\n", fps.datasel); 277 278 for (i = 0; i < ARRAY_SIZE(fps._st); i++) { 279 /* 280 * Recall that we need to use the current TOP-of-stack value to 281 * associate the _st[] index back to a physical register number, 282 * since tag word indices are physical register numbers. Then 283 * to get the tag value, we shift over two bits for each tag 284 * index, and then grab the bottom two bits. 285 */ 286 uint_t tag_index = (i + top) & 7; 287 uint_t tag_value = (fps.tag >> (tag_index * 2)) & 3; 288 289 fpru.reg = fps._st[i]; 290 mdb_printf("%%st%d 0x%04x.%04x%04x%04x%04x = %lg %s\n", 291 i, fpru.reg.exponent, 292 fpru.reg.significand[3], fpru.reg.significand[2], 293 fpru.reg.significand[1], fpru.reg.significand[0], 294 fpru.ld, fptag2str(tag_value)); 295 } 296 297 x86_pt_fpregs_sse_ctl(fps.mxcsr, fps.xstatus, buf, sizeof (buf)); 298 299 return (DCMD_OK); 300 } 301 302 /*ARGSUSED*/ 303 int 304 pt_getfpreg(mdb_tgt_t *t, mdb_tgt_tid_t tid, ushort_t rd_num, 305 ushort_t rd_flags, mdb_tgt_reg_t *rp) 306 { 307 return (set_errno(ENOTSUP)); 308 } 309 310 /*ARGSUSED*/ 311 int 312 pt_putfpreg(mdb_tgt_t *t, mdb_tgt_tid_t tid, ushort_t rd_num, 313 ushort_t rd_flags, mdb_tgt_reg_t rval) 314 { 315 return (set_errno(ENOTSUP)); 316 } 317 318 /*ARGSUSED*/ 319 void 320 pt_addfpregs(mdb_tgt_t *t) 321 { 322 /* not implemented */ 323 } 324 325 /*ARGSUSED*/ 326 int 327 pt_frameregs(void *arglim, uintptr_t pc, uint_t argc, const long *argv, 328 const mdb_tgt_gregset_t *gregs, boolean_t pc_faked) 329 { 330 return (set_errno(ENOTSUP)); 331 } 332 333 /*ARGSUSED*/ 334 const char * 335 pt_disasm(const GElf_Ehdr *ehp) 336 { 337 return ("ia32"); 338 } 339 340 /* 341 * Determine the return address for the current frame. 342 */ 343 int 344 pt_step_out(mdb_tgt_t *t, uintptr_t *p) 345 { 346 const lwpstatus_t *psp = &Pstatus(t->t_pshandle)->pr_lwp; 347 348 if (Pstate(t->t_pshandle) != PS_STOP) 349 return (set_errno(EMDB_TGTBUSY)); 350 351 return (mdb_ia32_step_out(t, p, psp->pr_reg[EIP], psp->pr_reg[EBP], 352 psp->pr_reg[UESP], pt_read_instr(t))); 353 } 354 355 /* 356 * Return the address of the next instruction following a call, or return -1 357 * and set errno to EAGAIN if the target should just single-step. 358 */ 359 int 360 pt_next(mdb_tgt_t *t, uintptr_t *p) 361 { 362 const lwpstatus_t *psp = &Pstatus(t->t_pshandle)->pr_lwp; 363 364 if (Pstate(t->t_pshandle) != PS_STOP) 365 return (set_errno(EMDB_TGTBUSY)); 366 367 return (mdb_ia32_next(t, p, psp->pr_reg[EIP], pt_read_instr(t))); 368 } 369