summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.c
blob: c9e3358af996269336791df750f7ed4ea3fa919a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * The KDI, or kernel/debugger interface, is used to allow the kernel and the
 * debugger to communicate.  These communications take two forms:
 *
 *  1. kernel to debugger.  Interfaces of this type are used by the kernel to
 *     inform the debugger of changes in the state of the system that need to
 *     be noted by the debugger.  For example, the kernel uses one of these
 *     interfaces to tell debugger that the set of currently-loaded modules
 *     has changed.
 *
 *  2. debugger to kernel.  Interfaces of this type are used by the debugger
 *     to extract information from the kernel that would otherwise be difficult
 *     to get, or to perform services that are specific to the machine being
 *     used.  An example of the former is the module iterator, which is needed
 *     to allow symbol resolution, but which needs to resolve symbols prior
 *     to the iteration.  The latter class include machine-specific or
 *     cpu-type-specific functions, such as the I-cache flusher.  By directly
 *     using the kernel versions of these functions, we avoid the need to
 *     include multiple versions of each function - one per cpu and/or machine -
 *     in kmdb.
 */

#include <sys/kdi_impl.h>

#include <kmdb/kmdb_kdi.h>
#include <kmdb/kmdb_dpi.h>
#include <kmdb/kmdb_kvm.h>
#include <kmdb/kmdb_promif.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

static int kdi_unload_request;

typedef struct mod_interp_data {
	int	(*mid_usercb)(struct modctl *, void *);
	void	*mid_userarg;
	jmp_buf mid_pcb;
	jmp_buf *mid_oldpcb;
} mod_interp_data_t;

static kmdb_auxv_t *kdi_auxv;

int
kmdb_kdi_mods_changed(void)
{
	return (mdb.m_kdi->kdi_mods_changed());
}

static int
kmdb_kdi_mod_interp(struct modctl *mp, void *arg)
{
	mod_interp_data_t *mid = arg;
	int rc;

	kmdb_dpi_restore_fault_hdlr(mid->mid_oldpcb);
	rc = mid->mid_usercb(mp, mid->mid_userarg);
	mid->mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid->mid_pcb);

	return (rc);
}

/*
 * We need to protect ourselves against any problems that may occur while
 * executing the module iterator, currently located in krtld.  If, for
 * example, one of the next pointers in the module list points to an invalid
 * address, we don't want kmdb to explode.  As such, we protect ourselves
 * with the DPI fault-protection routines.  We don't want our fault-protection
 * callback to protect the callback that the kmdb consumer provided, so we
 * provide our own interposition callback that removes our fault-protector
 * before invoking the user's callback.
 */
int
kmdb_kdi_mod_iter(int (*cb)(struct modctl *, void *), void *arg)
{
	mod_interp_data_t mid;
	int rc;

	if (setjmp(mid.mid_pcb) != 0) {
		/* We took a fault while iterating through the modules */
		kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);
		return (-1);
	}

	mid.mid_usercb = cb;
	mid.mid_userarg = arg;
	mid.mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid.mid_pcb);

	rc = mdb.m_kdi->kdi_mod_iter(kmdb_kdi_mod_interp, &mid);

	kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);

	return (rc);
}

int
kmdb_kdi_mod_isloaded(struct modctl *modp)
{
	return (mdb.m_kdi->kdi_mod_isloaded(modp));
}

int
kmdb_kdi_mod_haschanged(struct modctl *mc1, struct module *mp1,
    struct modctl *mc2, struct module *mp2)
{
	return (mdb.m_kdi->kdi_mod_haschanged(mc1, mp1, mc2, mp2));
}

static ssize_t
kdi_prw(void *buf, size_t nbytes, physaddr_t addr, int (*rw)(caddr_t, size_t,
    physaddr_t, size_t *))
{
	size_t sz;
	int rc;

	kmdb_dpi_flush_slave_caches();
	if ((rc = rw(buf, nbytes, addr, &sz)) != 0)
		return (set_errno(rc));

	return (sz);
}

ssize_t
kmdb_kdi_pread(void *buf, size_t nbytes, physaddr_t addr)
{
	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pread));
}

ssize_t
kmdb_kdi_pwrite(void *buf, size_t nbytes, physaddr_t addr)
{
	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pwrite));
}

void
kmdb_kdi_flush_caches(void)
{
	mdb.m_kdi->kdi_flush_caches();
}

int
kmdb_kdi_get_unload_request(void)
{
	return (kdi_unload_request);
}

void
kmdb_kdi_set_unload_request(void)
{
	kdi_unload_request = 1;
}

int
kmdb_kdi_get_flags(void)
{
	uint_t flags = 0;

	if (mdb.m_flags & MDB_FL_NOCTF)
		flags |= KMDB_KDI_FL_NOCTF;
	if (mdb.m_flags & MDB_FL_NOMODS)
		flags |= KMDB_KDI_FL_NOMODS;

	return (flags);
}

size_t
kmdb_kdi_range_is_nontoxic(uintptr_t va, size_t sz, int write)
{
	return (mdb.m_kdi->kdi_range_is_nontoxic(va, sz, write));
}

void
kmdb_kdi_system_claim(void)
{
	(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_claim, 0, NULL);
	kmdb_prom_debugger_entry();
}

void
kmdb_kdi_system_release(void)
{
	kmdb_prom_debugger_exit();

	if (mdb.m_kdi->kdi_system_release != NULL) {
		(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_release,
		    0, NULL);
	}
}

struct cons_polledio *
kmdb_kdi_get_polled_io(void)
{
	return (mdb.m_kdi->kdi_get_polled_io());
}

void
kmdb_kdi_kmdb_enter(void)
{
	mdb.m_kdi->kdi_kmdb_enter();
}

int
kmdb_kdi_vtop(uintptr_t va, physaddr_t *pap)
{
	jmp_buf pcb, *oldpcb;
	int rc = 0;

	if (setjmp(pcb) == 0) {
		int err;

		oldpcb = kmdb_dpi_set_fault_hdlr(&pcb);

		if ((err = mdb.m_kdi->kdi_vtop(va, pap)) != 0)
			rc = set_errno(err == ENOENT ? EMDB_NOMAP : err);
	} else {
		/* We faulted during the translation */
		rc = set_errno(EMDB_NOMAP);
	}

	kmdb_dpi_restore_fault_hdlr(oldpcb);

	return (rc);
}

kdi_dtrace_state_t
kmdb_kdi_dtrace_get_state(void)
{
	return (mdb.m_kdi->kdi_dtrace_get_state());
}

int
kmdb_kdi_dtrace_set(int state)
{
	int err;

	if ((err = mdb.m_kdi->kdi_dtrace_set(state)) != 0)
		return (set_errno(err));

	return (0);
}

/*
 * This function is to be called only during kmdb initialization, as it
 * uses the running kernel for symbol translation facilities.
 */
uintptr_t
kmdb_kdi_lookup_by_name(char *modname, char *symname)
{
	ASSERT(kmdb_dpi_get_state(NULL) == DPI_STATE_INIT);

	return (kdi_auxv->kav_lookup_by_name(modname, symname));
}

void
kmdb_kdi_init(kdi_t *kdi, kmdb_auxv_t *kav)
{
	mdb.m_kdi = kdi;
	mdb.m_pagesize = kav->kav_pagesize;

	kdi_unload_request = 0;

	kdi_auxv = kav;

	kmdb_kdi_init_isadep(kdi, kav);
}

void
kmdb_kdi_end_init(void)
{
	kdi_auxv = NULL;
}