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