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 2023 Oxide Computer Company
  29  */
  30 
  31 /*
  32  * Consolidated routines that are shared between the 32-bit and 64-bit x86 mdb
  33  * proc targets.
  34  */
  35 
  36 #include <mdb/mdb_proc.h>
  37 #include <mdb/mdb_err.h>
  38 #include <mdb/proc_x86util.h>
  39 #include <mdb/mdb.h>
  40 
  41 #include <libproc.h>
  42 #include <sys/fp.h>
  43 #include <ieeefp.h>
  44 #include <sys/sysmacros.h>
  45 
  46 const char *
  47 fpcw2str(uint32_t cw, char *buf, size_t nbytes)
  48 {
  49         char *end = buf + nbytes;
  50         char *p = buf;
  51 
  52         buf[0] = '\0';
  53 
  54         /*
  55          * Decode all exception masks in the x87 FPU Control Word.
  56          *
  57          * See here:
  58          * Intel® 64 and IA-32 Architectures Software Developer’s Manual,
  59          * Volume 1: Basic Architecture, 8.1.5 x87 FPU Control Word
  60          */
  61         if (cw & FPIM)      /* Invalid operation mask. */
  62                 p += mdb_snprintf(p, (size_t)(end - p), "|IM");
  63         if (cw & FPDM)      /* Denormalized operand mask. */
  64                 p += mdb_snprintf(p, (size_t)(end - p), "|DM");
  65         if (cw & FPZM)      /* Zero divide mask. */
  66                 p += mdb_snprintf(p, (size_t)(end - p), "|ZM");
  67         if (cw & FPOM)      /* Overflow mask. */
  68                 p += mdb_snprintf(p, (size_t)(end - p), "|OM");
  69         if (cw & FPUM)      /* Underflow mask. */
  70                 p += mdb_snprintf(p, (size_t)(end - p), "|UM");
  71         if (cw & FPPM)      /* Precision mask. */
  72                 p += mdb_snprintf(p, (size_t)(end - p), "|PM");
  73 
  74         /*
  75          * Decode precision control options.
  76          */
  77         switch (cw & FPPC) {
  78         case FPSIG24:
  79                 /* 24-bit significand, single precision. */
  80                 p += mdb_snprintf(p, (size_t)(end - p), "|SIG24");
  81                 break;
  82         case FPSIG53:
  83                 /* 53-bit significand, double precision. */
  84                 p += mdb_snprintf(p, (size_t)(end - p), "|SIG53");
  85                 break;
  86         case FPSIG64:
  87                 /* 64-bit significand, double extended precision. */
  88                 p += mdb_snprintf(p, (size_t)(end - p), "|SIG64");
  89                 break;
  90         default:
  91                 /*
  92                  * Should never happen.
  93                  * Value 0x00000100 is 'Reserved'.
  94                  */
  95                 break;
  96         }
  97 
  98         /*
  99          * Decode rounding control options.
 100          */
 101         switch (cw & FPRC) {
 102         case FPRTN:
 103                 /* Round to nearest, or to even if equidistant. */
 104                 p += mdb_snprintf(p, (size_t)(end - p), "|RTN");
 105                 break;
 106         case FPRD:
 107                 /* Round down. */
 108                 p += mdb_snprintf(p, (size_t)(end - p), "|RD");
 109                 break;
 110         case FPRU:
 111                 /* Round up. */
 112                 p += mdb_snprintf(p, (size_t)(end - p), "|RU");
 113                 break;
 114         case FPCHOP:
 115                 /* Truncate. */
 116                 p += mdb_snprintf(p, (size_t)(end - p), "|RTZ");
 117                 break;
 118         default:
 119                 /*
 120                  * This is a two-bit field.
 121                  * No other options left.
 122                  */
 123                 break;
 124         }
 125 
 126         /*
 127          * Decode infinity control options.
 128          *
 129          * This field has been retained for compatibility with
 130          * the 287 and earlier co-processors.
 131          * In the more modern FPUs, this bit is disregarded and
 132          * both -infinity and +infinity are respected.
 133          * Comment source: SIMPLY FPU by Raymond Filiatreault
 134          */
 135         switch (cw & FPIC) {
 136         case FPP:
 137                 /*
 138                  * Projective infinity.
 139                  * Both -infinity and +infinity are treated as
 140                  * unsigned infinity.
 141                  */
 142                 p += mdb_snprintf(p, (size_t)(end - p), "|P");
 143                 break;
 144         case FPA:
 145                 /*
 146                  * Affine infinity.
 147                  * Respects both -infinity and +infinity.
 148                  */
 149                 p += mdb_snprintf(p, (size_t)(end - p), "|A");
 150                 break;
 151         default:
 152                 /*
 153                  * This is a one-bit field.
 154                  * No other options left.
 155                  */
 156                 break;
 157         }
 158 
 159         if (cw & WFPB17)
 160                 p += mdb_snprintf(p, (size_t)(end - p), "|WFPB17");
 161         if (cw & WFPB24)
 162                 p += mdb_snprintf(p, (size_t)(end - p), "|WFPB24");
 163 
 164         if (buf[0] == '|')
 165                 return (buf + 1);
 166 
 167         return ("0");
 168 }
 169 
 170 const char *
 171 fpsw2str(uint32_t cw, char *buf, size_t nbytes)
 172 {
 173         char *end = buf + nbytes;
 174         char *p = buf;
 175 
 176         buf[0] = '\0';
 177 
 178         /*
 179          * Decode all masks in the 80387 status word.
 180          */
 181         if (cw & FPS_IE)
 182                 p += mdb_snprintf(p, (size_t)(end - p), "|IE");
 183         if (cw & FPS_DE)
 184                 p += mdb_snprintf(p, (size_t)(end - p), "|DE");
 185         if (cw & FPS_ZE)
 186                 p += mdb_snprintf(p, (size_t)(end - p), "|ZE");
 187         if (cw & FPS_OE)
 188                 p += mdb_snprintf(p, (size_t)(end - p), "|OE");
 189         if (cw & FPS_UE)
 190                 p += mdb_snprintf(p, (size_t)(end - p), "|UE");
 191         if (cw & FPS_PE)
 192                 p += mdb_snprintf(p, (size_t)(end - p), "|PE");
 193         if (cw & FPS_SF)
 194                 p += mdb_snprintf(p, (size_t)(end - p), "|SF");
 195         if (cw & FPS_ES)
 196                 p += mdb_snprintf(p, (size_t)(end - p), "|ES");
 197         if (cw & FPS_C0)
 198                 p += mdb_snprintf(p, (size_t)(end - p), "|C0");
 199         if (cw & FPS_C1)
 200                 p += mdb_snprintf(p, (size_t)(end - p), "|C1");
 201         if (cw & FPS_C2)
 202                 p += mdb_snprintf(p, (size_t)(end - p), "|C2");
 203         if (cw & FPS_C3)
 204                 p += mdb_snprintf(p, (size_t)(end - p), "|C3");
 205         if (cw & FPS_B)
 206                 p += mdb_snprintf(p, (size_t)(end - p), "|B");
 207 
 208         if (buf[0] == '|')
 209                 return (buf + 1);
 210 
 211         return ("0");
 212 }
 213 
 214 const char *
 215 fpmxcsr2str(uint32_t mxcsr, char *buf, size_t nbytes)
 216 {
 217         char *end = buf + nbytes;
 218         char *p = buf;
 219 
 220         buf[0] = '\0';
 221 
 222         /*
 223          * Decode the MXCSR word
 224          */
 225         if (mxcsr & SSE_IE)
 226                 p += mdb_snprintf(p, (size_t)(end - p), "|IE");
 227         if (mxcsr & SSE_DE)
 228                 p += mdb_snprintf(p, (size_t)(end - p), "|DE");
 229         if (mxcsr & SSE_ZE)
 230                 p += mdb_snprintf(p, (size_t)(end - p), "|ZE");
 231         if (mxcsr & SSE_OE)
 232                 p += mdb_snprintf(p, (size_t)(end - p), "|OE");
 233         if (mxcsr & SSE_UE)
 234                 p += mdb_snprintf(p, (size_t)(end - p), "|UE");
 235         if (mxcsr & SSE_PE)
 236                 p += mdb_snprintf(p, (size_t)(end - p), "|PE");
 237 
 238         if (mxcsr & SSE_DAZ)
 239                 p += mdb_snprintf(p, (size_t)(end - p), "|DAZ");
 240 
 241         if (mxcsr & SSE_IM)
 242                 p += mdb_snprintf(p, (size_t)(end - p), "|IM");
 243         if (mxcsr & SSE_DM)
 244                 p += mdb_snprintf(p, (size_t)(end - p), "|DM");
 245         if (mxcsr & SSE_ZM)
 246                 p += mdb_snprintf(p, (size_t)(end - p), "|ZM");
 247         if (mxcsr & SSE_OM)
 248                 p += mdb_snprintf(p, (size_t)(end - p), "|OM");
 249         if (mxcsr & SSE_UM)
 250                 p += mdb_snprintf(p, (size_t)(end - p), "|UM");
 251         if (mxcsr & SSE_PM)
 252                 p += mdb_snprintf(p, (size_t)(end - p), "|PM");
 253 
 254         if ((mxcsr & SSE_RC) == (SSE_RD|SSE_RU))
 255                 p += mdb_snprintf(p, (size_t)(end - p), "|RTZ");
 256         else if (mxcsr & SSE_RD)
 257                 p += mdb_snprintf(p, (size_t)(end - p), "|RD");
 258         else if (mxcsr & SSE_RU)
 259                 p += mdb_snprintf(p, (size_t)(end - p), "|RU");
 260         else
 261                 p += mdb_snprintf(p, (size_t)(end - p), "|RTN");
 262 
 263         if (mxcsr & SSE_FZ)
 264                 p += mdb_snprintf(p, (size_t)(end - p), "|FZ");
 265 
 266         if (buf[0] == '|')
 267                 return (buf + 1);
 268         return ("0");
 269 }
 270 
 271 const char *
 272 fptag2str(uint32_t val)
 273 {
 274         /*
 275          * Array of strings corresponding to FPU tag word values (see
 276          * section 7.3.6 of the Intel Programmer's Reference Manual).
 277          */
 278         const char *tag_strings[] = { "valid", "zero", "special", "empty" };
 279 
 280         if (val >= ARRAY_SIZE(tag_strings)) {
 281                 return ("unknown");
 282         }
 283 
 284         return (tag_strings[val]);
 285 }
 286 
 287 static uintptr_t
 288 xregs_data_ptr(const prxregset_hdr_t *prx, const prxregset_info_t *info)
 289 {
 290         uintptr_t base = (uintptr_t)prx;
 291         return (base + info->pri_offset);
 292 }
 293 
 294 static boolean_t
 295 xregs_valid_data(const prxregset_hdr_t *prx, const prxregset_info_t *info,
 296     size_t exp_size, const char *type)
 297 {
 298         size_t last_byte;
 299 
 300         if (info->pri_size != exp_size) {
 301                 mdb_warn("%s has unexpeced size 0x%lx, expected 0x%lx -- "
 302                     "cannot use\n", type, info->pri_size, exp_size);
 303                 return (B_FALSE);
 304         }
 305 
 306         last_byte = (size_t)info->pri_size + (size_t)info->pri_offset;
 307         if (last_byte < MIN(info->pri_size, info->pri_offset)) {
 308                 mdb_warn("%s size 0x%lx and offset 0x%lx appear to overflow -- "
 309                     "canot use\n", type, info->pri_size, info->pri_offset);
 310                 return (B_FALSE);
 311         }
 312 
 313         return (B_TRUE);
 314 }
 315 
 316 static const char *
 317 fp_type_to_str(x86_vector_type_t type)
 318 {
 319         switch (type) {
 320         case XMM:
 321                 return ("128-bit %xmm");
 322         case YMM:
 323                 return ("256-bit %ymm");
 324         case ZMM:
 325                 return ("512-bit %zmm");
 326         default:
 327                 return ("unknown");
 328         }
 329 }
 330 
 331 /*
 332  * Go through the xregs data that we have and make sure that it makes sense for
 333  * printing. In particular we need to make sure:
 334  *
 335  *  o The structure type is what we expect
 336  *  o That its overall size is correct
 337  *  o That we can find the expected set of data pointers that should be here
 338  *  o That the information pointers actually make sense and their contents are
 339  *    both the correct size and within the overall structure. Note, we do not
 340  *    check for overlapping data regions right now, meaning that some weird
 341  *    notes may still lead to weird data.
 342  */
 343 static boolean_t
 344 pt_xregs_process(const prxregset_hdr_t *prx, size_t found_size,
 345     x86_xregs_info_t *xinfo)
 346 {
 347         bzero(xinfo, sizeof (*xinfo));
 348 
 349         if (prx->pr_type != PR_TYPE_XSAVE) {
 350                 mdb_warn("prxregset has unknown type: 0x%x\n -- falling back "
 351                     "to fpregset_t\n", prx->pr_type);
 352                 return (B_FALSE);
 353         }
 354 
 355         if (prx->pr_size < found_size) {
 356                 mdb_warn("prxregset has greater size than we were given: "
 357                     "found 0x%lx, have 0x%lx\n", prx->pr_size, found_size);
 358                 return (B_FALSE);
 359         }
 360 
 361         for (uint32_t i = 0; i < prx->pr_ninfo; i++) {
 362                 switch (prx->pr_info[i].pri_type) {
 363                 case PRX_INFO_XCR:
 364                         if (xregs_valid_data(prx, &prx->pr_info[i],
 365                             sizeof (prxregset_xcr_t), "xcr")) {
 366                                 xinfo->xri_xcr = (void *)xregs_data_ptr(prx,
 367                                     &prx->pr_info[i]);
 368                         }
 369                         break;
 370                 case PRX_INFO_XSAVE:
 371                         if (xregs_valid_data(prx, &prx->pr_info[i],
 372                             sizeof (prxregset_xsave_t), "xsave")) {
 373                                 xinfo->xri_xsave = (void *)xregs_data_ptr(prx,
 374                                     &prx->pr_info[i]);
 375                         }
 376                         break;
 377                 case PRX_INFO_YMM:
 378                         if (xregs_valid_data(prx, &prx->pr_info[i],
 379                             sizeof (prxregset_ymm_t), "ymm")) {
 380                                 xinfo->xri_ymm = (void *)xregs_data_ptr(prx,
 381                                     &prx->pr_info[i]);
 382                         }
 383                         break;
 384                 case PRX_INFO_OPMASK:
 385                         if (xregs_valid_data(prx, &prx->pr_info[i],
 386                             sizeof (prxregset_opmask_t), "opmask")) {
 387                                 xinfo->xri_opmask = (void *)xregs_data_ptr(prx,
 388                                     &prx->pr_info[i]);
 389                         }
 390                         break;
 391                 case PRX_INFO_ZMM:
 392                         if (xregs_valid_data(prx, &prx->pr_info[i],
 393                             sizeof (prxregset_zmm_t), "zmm")) {
 394                                 xinfo->xri_zmm = (void *)xregs_data_ptr(prx,
 395                                     &prx->pr_info[i]);
 396                         }
 397                         break;
 398                 case PRX_INFO_HI_ZMM:
 399                         if (xregs_valid_data(prx, &prx->pr_info[i],
 400                             sizeof (prxregset_hi_zmm_t), "hi_zmm")) {
 401                                 xinfo->xri_hi_zmm = (void *)xregs_data_ptr(prx,
 402                                     &prx->pr_info[i]);
 403                         }
 404                         break;
 405                 default:
 406                         mdb_warn("ignoring unexpected xreg info type: 0x%x\n",
 407                             prx->pr_info[i].pri_type);
 408                         break;
 409                 }
 410         }
 411 
 412         /*
 413          * Now that we have gotten this far, we go and figure out what the
 414          * largest type of information we actually have is. We check from the
 415          * simplest to the most complex as to see the more complex state
 416          * requires having the more basic state, due to how Intel designed the
 417          * xsave state.
 418          */
 419         if (xinfo->xri_xsave == NULL) {
 420                 mdb_warn("missing required xsave information: xregs not "
 421                     "usable\n");
 422                 return (B_FALSE);
 423         }
 424 
 425         xinfo->xri_type = XMM;
 426         if (xinfo->xri_ymm != NULL) {
 427                 uint_t nzmm = 0;
 428                 if (xinfo->xri_opmask != NULL)
 429                         nzmm++;
 430                 if (xinfo->xri_zmm != NULL)
 431                         nzmm++;
 432                 if (xinfo->xri_hi_zmm != NULL)
 433                         nzmm++;
 434                 if (nzmm == 3) {
 435                         xinfo->xri_type = ZMM;
 436                 } else if (nzmm == 0) {
 437                         xinfo->xri_type = YMM;
 438                 } else {
 439                         mdb_warn("encountered mismatched AVX-512 components, "
 440                             "defaulting back to YMM\n");
 441                         mdb_warn("found opmask %s, zmm %s, hi zmm %s\n",
 442                             xinfo->xri_opmask != NULL ? "present" : "missing",
 443                             xinfo->xri_zmm != NULL ? "present" : "missing",
 444                             xinfo->xri_hi_zmm != NULL ? "present" : "missing");
 445                 }
 446         }
 447 
 448         return (B_TRUE);
 449 }
 450 
 451 static void
 452 pt_xreg_single_vector(const upad128_t *xmm, const upad128_t *ymm,
 453     const upad256_t *zmm, uint32_t num)
 454 {
 455 
 456         if (zmm != NULL) {
 457                 mdb_printf("%%zmm%u%s[511:384] 0x%08x %08x %08x %08x\n"
 458                     "       [383:256] 0x%08x %08x %08x %08x\n", num,
 459                     num >= 10 ? " " : "  ",
 460                     zmm->_l[7], zmm->_l[6], zmm->_l[5], zmm->_l[4],
 461                     zmm->_l[3], zmm->_l[2], zmm->_l[1], zmm->_l[0]);
 462         }
 463 
 464         if (ymm != NULL) {
 465                 mdb_printf("%%ymm%u%s[255:128] 0x%08x %08x %08x %08x\n",
 466                     num, num >= 10 ? " " : "  ",
 467                     ymm->_l[3], ymm->_l[2], ymm->_l[1], ymm->_l[0]);
 468         }
 469 
 470         if (xmm != NULL) {
 471                 mdb_printf("%%xmm%u%s[127:0]   0x%08x %08x %08x %08x\n",
 472                     num, num >= 10 ? " " : "  ",
 473                     xmm->_l[3], xmm->_l[2], xmm->_l[1], xmm->_l[0]);
 474         }
 475 
 476         /*
 477          * Insert output spacing if we exceed more than one line which happens
 478          * if ymm state is present.
 479          */
 480         if (ymm != NULL) {
 481                 mdb_printf("\n");
 482         }
 483 }
 484 
 485 /*
 486  * Variant of the above, but all of the data is one single register. This is
 487  * only used for the high zmm registers which are only present on amd64.
 488  */
 489 #ifdef __amd64
 490 static void
 491 pt_xreg_single_u512(const upad512_t *zmm, uint32_t num)
 492 {
 493         mdb_printf("%%zmm%u%s[511:384] 0x%08x %08x %08x %08x\n"
 494             "       [383:256] 0x%08x %08x %08x %08x\n", num,
 495             num >= 10 ? " " : "  ",
 496             zmm->_l[15], zmm->_l[14], zmm->_l[13], zmm->_l[12],
 497             zmm->_l[11], zmm->_l[10], zmm->_l[9], zmm->_l[8]);
 498 
 499         mdb_printf("%%zmm%u%s[255:128] 0x%08x %08x %08x %08x\n",
 500             num, num >= 10 ? " " : "  ",
 501             zmm->_l[7], zmm->_l[6], zmm->_l[5], zmm->_l[4]);
 502 
 503         mdb_printf("%%zmm%u%s[127:0]   0x%08x %08x %08x %08x\n",
 504             num, num >= 10 ? " " : "  ",
 505             zmm->_l[3], zmm->_l[2], zmm->_l[1], zmm->_l[0]);
 506 
 507         mdb_printf("\n");
 508 }
 509 #endif  /* __amd64 */
 510 
 511 /*
 512  * There are two different cases that we need to consider for vector printing.
 513  * The first 16 FPU registers are shadowed as the low bits of xmm0 overlap with
 514  * ymm0, overlap with zmm0.
 515  */
 516 static void
 517 pt_xregs_vectors(const x86_xregs_info_t *xinfo)
 518 {
 519         size_t nregs = ARRAY_SIZE(xinfo->xri_xsave->prx_fx_xmm);
 520         for (size_t i = 0; i < nregs; i++) {
 521                 switch (xinfo->xri_type) {
 522                 case XMM:
 523                         pt_xreg_single_vector(&xinfo->xri_xsave->prx_fx_xmm[i],
 524                             NULL, NULL, i);
 525                         break;
 526                 case YMM:
 527                         pt_xreg_single_vector(&xinfo->xri_xsave->prx_fx_xmm[i],
 528                             &xinfo->xri_ymm->prx_ymm[i], NULL, i);
 529                         break;
 530                 case ZMM:
 531                         pt_xreg_single_vector(&xinfo->xri_xsave->prx_fx_xmm[i],
 532                             &xinfo->xri_ymm->prx_ymm[i],
 533                             &xinfo->xri_zmm->prx_zmm[i], i);
 534                         break;
 535                 }
 536         }
 537 
 538         /*
 539          * If we have ZMM state, next print the remaining 16 registers and then
 540          * the 8 opmask registers. Note, we only have the high ZMM registers on
 541          * 64-bit processes.
 542          */
 543         if (xinfo->xri_type == ZMM) {
 544 #ifdef __amd64
 545                 nregs = ARRAY_SIZE(xinfo->xri_hi_zmm->prx_hi_zmm);
 546                 for (size_t i = 0; i < nregs; i++) {
 547                         pt_xreg_single_u512(&xinfo->xri_hi_zmm->prx_hi_zmm[i],
 548                             i + 16);
 549                 }
 550 #endif  /* __amd64 */
 551 
 552                 mdb_printf("%%k0  0x%016x\t\t%%k1  0x%016x\n",
 553                     xinfo->xri_opmask->prx_opmask[0],
 554                     xinfo->xri_opmask->prx_opmask[1]);
 555                 mdb_printf("%%k2  0x%016x\t\t%%k3  0x%016x\n",
 556                     xinfo->xri_opmask->prx_opmask[2],
 557                     xinfo->xri_opmask->prx_opmask[3]);
 558                 mdb_printf("%%k4  0x%016x\t\t%%k5  0x%016x\n",
 559                     xinfo->xri_opmask->prx_opmask[4],
 560                     xinfo->xri_opmask->prx_opmask[5]);
 561                 mdb_printf("%%k6  0x%016x\t\t%%k7  0x%016x\n",
 562                     xinfo->xri_opmask->prx_opmask[6],
 563                     xinfo->xri_opmask->prx_opmask[7]);
 564 
 565                 mdb_printf("\n");
 566         }
 567 }
 568 
 569 int
 570 x86_pt_fpregs_common(uintptr_t addr, uint_t flags, int argc,
 571     prfpregset_t *fprsp)
 572 {
 573         mdb_tgt_t *t = mdb.m_target;
 574         mdb_tgt_tid_t tid;
 575         prxregset_t *xregs = NULL;
 576         size_t xregsize = 0;
 577         x86_xregs_info_t xinfo;
 578         x86_vector_type_t vector_type = XMM;
 579 
 580         if (argc != 0)
 581                 return (DCMD_USAGE);
 582 
 583         if (t->t_pshandle == NULL || Pstate(t->t_pshandle) == PS_UNDEAD) {
 584                 mdb_warn("no process active\n");
 585                 return (DCMD_ERR);
 586         }
 587 
 588         if (Pstate(t->t_pshandle) == PS_LOST) {
 589                 mdb_warn("debugger has lost control of process\n");
 590                 return (DCMD_ERR);
 591         }
 592 
 593         if (flags & DCMD_ADDRSPEC)
 594                 tid = (mdb_tgt_tid_t)addr;
 595         else
 596                 tid = PTL_TID(t);
 597 
 598         /*
 599          * We ultimately need both the xregs and the fpregs. The fpregs have
 600          * included synthetic-kernel created state that is not part of the FPU
 601          * (the status / xstatus bits). If we find the xregs state, then we
 602          * focus on using its data in lieu of the standard fxsave piece.
 603          */
 604         if (PTL_GETFPREGS(t, tid, fprsp) != 0) {
 605                 mdb_warn("failed to get floating point registers");
 606                 return (DCMD_ERR);
 607         }
 608 
 609         bzero(&xinfo, sizeof (x86_xregs_info_t));
 610         if (PTL_GETXREGS(t, tid, &xregs, &xregsize) == 0) {
 611                 prxregset_hdr_t *prx = (prxregset_hdr_t *)xregs;
 612                 if (!pt_xregs_process(prx, xregsize, &xinfo)) {
 613                         PTL_FREEXREGS(t, xregs, xregsize);
 614                         xregs = NULL;
 615                 } else {
 616                         vector_type = xinfo.xri_type;
 617                 }
 618         } else if (errno != ENOENT && errno != ENODATA && errno != ENOTSUP) {
 619                 mdb_warn("failed to get xregs");
 620         }
 621 
 622         /*
 623          * We ultimately need both the xregs and the fpregs. The fpregs have
 624          * included synthetic-kernel created state that is not part of the FPU
 625          * (the status / xstatus bits). If we find the xregs state, then we
 626          * focus on using its data in lieu of the standard fxsave piece.
 627          */
 628         if (PTL_GETFPREGS(t, tid, fprsp) != 0) {
 629                 mdb_warn("failed to get floating point registers");
 630                 return (DCMD_ERR);
 631         }
 632 
 633         bzero(&xinfo, sizeof (x86_xregs_info_t));
 634         if (PTL_GETXREGS(t, tid, &xregs, &xregsize) == 0) {
 635                 prxregset_hdr_t *prx = (prxregset_hdr_t *)xregs;
 636                 if (!pt_xregs_process(prx, xregsize, &xinfo)) {
 637                         PTL_FREEXREGS(t, xregs, xregsize);
 638                         xregs = NULL;
 639                 } else {
 640                         vector_type = xinfo.xri_type;
 641                 }
 642         } else if (errno != ENOENT && errno != ENODATA && errno != ENOTSUP) {
 643                 mdb_warn("failed to get xregs");
 644         }
 645 
 646         /*
 647          * As we only support the amd64 kernel, we basically phrase the FPU the
 648          * same way regardless of whether it is 32-bit or 64-bit now.
 649          */
 650         mdb_printf("x86 FPU with %s registers\n", fp_type_to_str(vector_type));
 651         if (xinfo.xri_xcr != NULL) {
 652                 mdb_printf("xcr0\t\t0x%lx\n", xinfo.xri_xcr->prx_xcr_xcr0);
 653                 mdb_printf("xfd\t\t0x%lx\n", xinfo.xri_xcr->prx_xcr_xfd);
 654         }
 655 
 656         if (xinfo.xri_xsave != NULL) {
 657                 mdb_printf("xstate_bv\t0x%lx\n",
 658                     xinfo.xri_xsave->prx_xsh_xstate_bv);
 659                 mdb_printf("xcomp_bv\t0x%lx\n",
 660                     xinfo.xri_xsave->prx_xsh_xcomp_bv);
 661 
 662                 mdb_printf("\n");
 663                 /*
 664                  * xsave is required for us to use the xregset, so from here as
 665                  * it to print vectors.
 666                  */
 667                 pt_xregs_vectors(&xinfo);
 668         } else {
 669                 size_t nregs = ARRAY_SIZE(fprsp->fp_reg_set.fpchip_state.xmm);
 670                 for (uint32_t i = 0; i < nregs; i++) {
 671                         const upad128_t *u128 =
 672                             &fprsp->fp_reg_set.fpchip_state.xmm[i];
 673                         pt_xreg_single_vector(u128, NULL, NULL, i);
 674                 }
 675 
 676                 mdb_printf("\n");
 677         }
 678 
 679         if (xregs != NULL) {
 680                 PTL_FREEXREGS(t, xregs, xregsize);
 681         }
 682 
 683         return (DCMD_OK);
 684 }
 685 
 686 void
 687 x86_pt_fpregs_sse_ctl(uint32_t mxcsr, uint32_t xstatus, char *buf,
 688     size_t buflen)
 689 {
 690         mdb_printf("\nSSE Control State\n");
 691         mdb_printf("mxcsr  0x%04x (%s)\n", mxcsr,
 692             fpmxcsr2str(mxcsr, buf, buflen));
 693         mdb_printf("xcp    0x%04x (%s)\n", xstatus,
 694             fpmxcsr2str(xstatus, buf, buflen));
 695 }