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 *
fpcw2str(uint32_t cw,char * buf,size_t nbytes)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 *
fpsw2str(uint32_t cw,char * buf,size_t nbytes)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 *
fpmxcsr2str(uint32_t mxcsr,char * buf,size_t nbytes)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 *
fptag2str(uint32_t val)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
xregs_data_ptr(const prxregset_hdr_t * prx,const prxregset_info_t * info)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
xregs_valid_data(const prxregset_hdr_t * prx,const prxregset_info_t * info,size_t exp_size,const char * type)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 *
fp_type_to_str(x86_vector_type_t type)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
pt_xregs_process(const prxregset_hdr_t * prx,size_t found_size,x86_xregs_info_t * xinfo)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
pt_xreg_single_vector(const upad128_t * xmm,const upad128_t * ymm,const upad256_t * zmm,uint32_t num)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
pt_xreg_single_u512(const upad512_t * zmm,uint32_t num)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
pt_xregs_vectors(const x86_xregs_info_t * xinfo)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
x86_pt_fpregs_common(uintptr_t addr,uint_t flags,int argc,prfpregset_t * fprsp)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
x86_pt_fpregs_sse_ctl(uint32_t mxcsr,uint32_t xstatus,char * buf,size_t buflen)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