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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25/*
26 * Copyright (c) 2012, Joyent, Inc.  All rights reserved.
27 */
28
29/*
30 * Libkvm Kernel Target Intel component
31 *
32 * This file provides the Intel-dependent portion of the libkvm kernel target.
33 * For more details on the implementation refer to mdb_kvm.c.
34 */
35
36#include <mdb/mdb_target_impl.h>
37#include <mdb/mdb_kreg_impl.h>
38#include <mdb/mdb_errno.h>
39#include <mdb/mdb_err.h>
40#include <mdb/mdb_kvm.h>
41#include <mdb/mdb_ks.h>
42#include <mdb/mdb.h>
43#include <mdb/kvm_isadep.h>
44
45#include <sys/cpuvar.h>
46#include <sys/privmregs.h>
47
48int
49kt_getareg(mdb_tgt_t *t, mdb_tgt_tid_t tid,
50    const char *rname, mdb_tgt_reg_t *rp)
51{
52	const mdb_tgt_regdesc_t *rdp;
53	kt_data_t *kt = t->t_data;
54
55	if (tid != kt->k_tid)
56		return (set_errno(EMDB_NOREGS));
57
58	for (rdp = kt->k_rds; rdp->rd_name != NULL; rdp++) {
59		if (strcmp(rname, rdp->rd_name) == 0) {
60			*rp = kt->k_regs->kregs[rdp->rd_num];
61			if (rdp->rd_flags & MDB_TGT_R_32)
62				*rp &= 0xffffffffULL;
63			else if (rdp->rd_flags & MDB_TGT_R_16)
64				*rp &= 0xffffULL;
65			else if (rdp->rd_flags & MDB_TGT_R_8H)
66				*rp = (*rp & 0xff00ULL) >> 8;
67			else if (rdp->rd_flags & MDB_TGT_R_8L)
68				*rp &= 0xffULL;
69			return (0);
70		}
71	}
72
73	return (set_errno(EMDB_BADREG));
74}
75
76int
77kt_putareg(mdb_tgt_t *t, mdb_tgt_tid_t tid, const char *rname, mdb_tgt_reg_t r)
78{
79	const mdb_tgt_regdesc_t *rdp;
80	kt_data_t *kt = t->t_data;
81
82	if (tid != kt->k_tid)
83		return (set_errno(EMDB_NOREGS));
84
85	for (rdp = kt->k_rds; rdp->rd_name != NULL; rdp++) {
86		if (strcmp(rname, rdp->rd_name) == 0) {
87			if (rdp->rd_flags & MDB_TGT_R_32)
88				r &= 0xffffffffULL;
89			else if (rdp->rd_flags & MDB_TGT_R_16)
90				r &= 0xffffULL;
91			else if (rdp->rd_flags & MDB_TGT_R_8H)
92				r = (r & 0xffULL) << 8;
93			else if (rdp->rd_flags & MDB_TGT_R_8L)
94				r &= 0xffULL;
95
96			kt->k_regs->kregs[rdp->rd_num] = (kreg_t)r;
97			return (0);
98		}
99	}
100
101	return (set_errno(EMDB_BADREG));
102}
103
104int
105kt_kvmregs(mdb_tgt_t *t, uint_t cpuid, mdb_tgt_gregset_t *kregs)
106{
107	kt_data_t *kt = t->t_data;
108	privmregs_t mregs;
109	int ret;
110
111	if ((ret = kt->k_kb_ops->kb_getmregs(kt->k_cookie, cpuid, &mregs)) != 0)
112		return (ret);
113
114	kt_regs_to_kregs(&mregs.pm_gregs, kregs);
115	return (0);
116}
117
118static int
119kt_cpu2cpuid(uintptr_t cpup)
120{
121	cpu_t cpu;
122
123	if (mdb_vread(&cpu, sizeof (cpu_t), cpup) != sizeof (cpu_t))
124		return (-1);
125
126	return (cpu.cpu_id);
127}
128
129int
130kt_cpustack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
131{
132	mdb_tgt_t *t = mdb.m_target;
133	mdb_tgt_gregset_t regs;
134	intptr_t cpuid = 0;
135	uint_t verbose = 0;
136	int i;
137
138	if (flags & DCMD_ADDRSPEC) {
139		if ((cpuid = kt_cpu2cpuid(addr)) < 0) {
140			(void) set_errno(EMDB_NOMAP);
141			mdb_warn("failed to find cpuid for cpu at %p", addr);
142			return (DCMD_ERR);
143		}
144		flags &= ~DCMD_ADDRSPEC;
145	}
146
147
148	i = mdb_getopts(argc, argv,
149	    'c', MDB_OPT_UINTPTR, &cpuid,
150	    'v', MDB_OPT_SETBITS, 1, &verbose,
151	    NULL);
152
153	argc -= i;
154	argv += i;
155
156	if (kt_kvmregs(t, cpuid, &regs) != 0) {
157		mdb_warn("failed to get regs for cpu %d\n", cpuid);
158		return (DCMD_ERR);
159	}
160
161	/*
162	 * Tell the stack walker that we have regs.
163	 */
164	flags |= DCMD_ADDRSPEC;
165	addr = regs.kregs[KREG_FP];
166
167	if (verbose)
168		return (kt_stackv(addr, flags, argc, argv));
169	else
170		return (kt_stack(addr, flags, argc, argv));
171}
172
173/*ARGSUSED*/
174int
175kt_cpuregs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
176{
177	mdb_tgt_t *t = mdb.m_target;
178	mdb_tgt_gregset_t regs;
179	intptr_t cpuid = 0;
180	int i;
181
182	if (flags & DCMD_ADDRSPEC) {
183		if (argc != 0)
184			return (DCMD_USAGE);
185		if ((cpuid = kt_cpu2cpuid(addr)) < 0) {
186			(void) set_errno(EMDB_NOMAP);
187			mdb_warn("failed to find cpuid for cpu at %p", addr);
188			return (DCMD_ERR);
189		}
190	}
191
192
193	i = mdb_getopts(argc, argv,
194	    'c', MDB_OPT_UINTPTR, &cpuid,
195	    NULL);
196
197	argc -= i;
198	argv += i;
199
200	if (argc != 0)
201		return (DCMD_USAGE);
202
203	if (kt_kvmregs(t, cpuid, &regs) != 0) {
204		mdb_warn("failed to get regs for cpu %d\n", cpuid);
205		return (DCMD_ERR);
206	}
207
208	return (kt_regs((uintptr_t)&regs, flags, argc, argv));
209}
210