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 -- 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 -- falling back to fpregset_t\n", 358 prx->pr_size, found_size); 359 return (B_FALSE); 360 } 361 362 for (uint32_t i = 0; i < prx->pr_ninfo; i++) { 363 switch (prx->pr_info[i].pri_type) { 364 case PRX_INFO_XCR: 365 if (xregs_valid_data(prx, &prx->pr_info[i], 366 sizeof (prxregset_xcr_t), "xcr")) { 367 xinfo->xri_xcr = (void *)xregs_data_ptr(prx, 368 &prx->pr_info[i]); 369 } 370 break; 371 case PRX_INFO_XSAVE: 372 if (xregs_valid_data(prx, &prx->pr_info[i], 373 sizeof (prxregset_xsave_t), "xsave")) { 374 xinfo->xri_xsave = (void *)xregs_data_ptr(prx, 375 &prx->pr_info[i]); 376 } 377 break; 378 case PRX_INFO_YMM: 379 if (xregs_valid_data(prx, &prx->pr_info[i], 380 sizeof (prxregset_ymm_t), "ymm")) { 381 xinfo->xri_ymm = (void *)xregs_data_ptr(prx, 382 &prx->pr_info[i]); 383 } 384 break; 385 case PRX_INFO_OPMASK: 386 if (xregs_valid_data(prx, &prx->pr_info[i], 387 sizeof (prxregset_opmask_t), "opmask")) { 388 xinfo->xri_opmask = (void *)xregs_data_ptr(prx, 389 &prx->pr_info[i]); 390 } 391 break; 392 case PRX_INFO_ZMM: 393 if (xregs_valid_data(prx, &prx->pr_info[i], 394 sizeof (prxregset_zmm_t), "zmm")) { 395 xinfo->xri_zmm = (void *)xregs_data_ptr(prx, 396 &prx->pr_info[i]); 397 } 398 break; 399 case PRX_INFO_HI_ZMM: 400 if (xregs_valid_data(prx, &prx->pr_info[i], 401 sizeof (prxregset_hi_zmm_t), "hi_zmm")) { 402 xinfo->xri_hi_zmm = (void *)xregs_data_ptr(prx, 403 &prx->pr_info[i]); 404 } 405 break; 406 default: 407 mdb_warn("ignoring unexpected xreg info type: 0x%x\n", 408 prx->pr_info[i].pri_type); 409 break; 410 } 411 } 412 413 /* 414 * Now that we have gotten this far, we go and figure out what the 415 * largest type of information we actually have is. We check from the 416 * simplest to the most complex as to see the more complex state 417 * requires having the more basic state, due to how Intel designed the 418 * xsave state. 419 */ 420 if (xinfo->xri_xsave == NULL) { 421 mdb_warn("missing required xsave information: xregs not " 422 "usable -- falling back to fpregset_t\n"); 423 return (B_FALSE); 424 } 425 426 xinfo->xri_type = XMM; 427 if (xinfo->xri_ymm != NULL) { 428 xinfo->xri_type = YMM; 429 uint_t nzmm = 0; 430 if (xinfo->xri_opmask != NULL) 431 nzmm++; 432 if (xinfo->xri_zmm != NULL) 433 nzmm++; 434 if (xinfo->xri_hi_zmm != NULL) 435 nzmm++; 436 if (nzmm == 3) { 437 xinfo->xri_type = ZMM; 438 } else if (nzmm != 0) { 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 * As we only support the amd64 kernel, we basically phrase the FPU the 624 * same way regardless of whether it is a 32-bit or 64-bit process. 625 */ 626 mdb_printf("x86 FPU with %s registers\n", fp_type_to_str(vector_type)); 627 if (xinfo.xri_xcr != NULL) { 628 mdb_printf("xcr0\t\t0x%lx\n", xinfo.xri_xcr->prx_xcr_xcr0); 629 mdb_printf("xfd\t\t0x%lx\n", xinfo.xri_xcr->prx_xcr_xfd); 630 } 631 632 if (xinfo.xri_xsave != NULL) { 633 mdb_printf("xstate_bv\t0x%lx\n", 634 xinfo.xri_xsave->prx_xsh_xstate_bv); 635 mdb_printf("xcomp_bv\t0x%lx\n", 636 xinfo.xri_xsave->prx_xsh_xcomp_bv); 637 638 mdb_printf("\n"); 639 /* 640 * xsave is required for us to use the xregset, so from here as 641 * it to print vectors. 642 */ 643 pt_xregs_vectors(&xinfo); 644 } else { 645 size_t nregs = ARRAY_SIZE(fprsp->fp_reg_set.fpchip_state.xmm); 646 for (uint32_t i = 0; i < nregs; i++) { 647 const upad128_t *u128 = 648 &fprsp->fp_reg_set.fpchip_state.xmm[i]; 649 pt_xreg_single_vector(u128, NULL, NULL, i); 650 } 651 652 mdb_printf("\n"); 653 } 654 655 if (xregs != NULL) { 656 PTL_FREEXREGS(t, xregs, xregsize); 657 } 658 659 return (DCMD_OK); 660 } 661 662 void 663 x86_pt_fpregs_sse_ctl(uint32_t mxcsr, uint32_t xstatus, char *buf, 664 size_t buflen) 665 { 666 mdb_printf("\nSSE Control State\n"); 667 mdb_printf("mxcsr 0x%04x (%s)\n", mxcsr, 668 fpmxcsr2str(mxcsr, buf, buflen)); 669 mdb_printf("xcp 0x%04x (%s)\n", xstatus, 670 fpmxcsr2str(xstatus, buf, buflen)); 671 } 672