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
|
/*
* 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 is used to allow the kernel debugger to directly invoke various
* kernel functions. In some cases, such as with kdi_mod_iter(), the
* debugger needs to execute functions that use the kernel's linker bindings.
* In other cases, the implementation of the KDI functions vary by platform
* and/or by CPU. By embedding the implementation of these functions in
* the platmod/cpumod, we can avoid the need for platform-specific knowledge
* in the debugger, and can thus have a single debugger binary for all
* platforms.
*
* There are three classes of KDI function:
*
* 1. Normal - These are functions whose implementations are in the kernel for
* convenience. An example is the modctl iterator, kdi_mod_iter. Using the
* modules symbol, this function iterates through the kernel's modctl list,
* invoking a debugger-provided callback for each one. This function is in
* the KDI because the debugger needs to be able to execute it in order to
* enable symbol resolution. Without symbol resolution, the debugger can't
* locate the modules symbol. A chicken-and-egg problem results. We solve
* this problem by locating the module iterator in the kernel, where run-time
* linking solves the problem for us.
*
* 2. CPU-specific - Functions in this class have implementations that differ
* by CPU. For example, the crosscall delivery notification method differs
* between Cheetah and Jalapeno, necessitating a different implementation for
* each. By locating the KDI implementation of these functions in the
* cpumods, we automatically get the correct implementation, as krtld
* automatically loads the correct cpumod when it starts. The cpumods
* directly fill in their portion of the kdi_t, using the mandatory
* cpu_kdi_init cpumod entry point.
*
* 3. Platform-specific - Similar to the CPU-specific class, platform-specific
* KDI functions have implementations that differ from platform to platform.
* As such, the implementations live in the platmods. Further
* differentiating the platform-specific KDI functions from their
* CPU-dependent brethren, many directly invoke PROM functions. This poses
* a problem, as the platmods use the kernel's promif functions, rather than
* the lock-free kmdb versions. We provide an interposition layer for these
* platform-specific calls that disables the pre- and post-processing
* functions used by the kernel to implement kernel-specific functionality
* that must not be executed when kmdb has control of the machine. Platmods
* fill in a kdi_plat_t using their optional plat_kdi_init entry point.
* krtld provides wrapper functions which suspend the necessary functions in
* the promif layer before invoking the kdi_plat_t functions (if any).
*/
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/reboot.h>
#include <sys/kdi_impl.h>
#include <krtld/kobj_kdi.h>
#define KOBJ_KDI_MOD_IDLE 0
#define KOBJ_KDI_MOD_CHANGING 1
#define KOBJ_KDI_MOD_CHANGED 2
static int kobj_kdi_mod_state = KOBJ_KDI_MOD_IDLE;
extern int standalone;
cons_polledio_t *
kobj_kdi_get_polled_io(void)
{
cons_polledio_t **polled_io = &cons_polledio;
return (polled_io == NULL ? NULL : *polled_io);
}
int
kobj_kdi_mod_iter(int (*func)(struct modctl *, void *), void *arg)
{
int rc;
if (standalone) {
struct modctl_list *lp, **lpp;
for (lpp = kobj_linkmaps; *lpp != NULL; lpp++) {
for (lp = *lpp; lp != NULL; lp = lp->modl_next) {
if ((rc = func(lp->modl_modp, arg)) != 0)
return (rc);
}
}
} else {
struct modctl *modp = &modules;
do {
if ((rc = func(modp, arg)) != 0)
return (rc);
} while ((modp = modp->mod_next) != &modules);
}
return (0);
}
int
kobj_kdi_mod_isloaded(struct modctl *modp)
{
return (modp->mod_mp != NULL);
}
int
kobj_kdi_mods_changed(void)
{
int state;
if ((state = kobj_kdi_mod_state) == KOBJ_KDI_MOD_CHANGED)
kobj_kdi_mod_state = KOBJ_KDI_MOD_IDLE;
return (state != KOBJ_KDI_MOD_IDLE);
}
/*ARGSUSED1*/
void
kobj_kdi_mod_notify(uint_t why, struct modctl *what)
{
switch (why) {
case KOBJ_NOTIFY_MODLOADING:
kobj_kdi_mod_state = KOBJ_KDI_MOD_CHANGING;
break;
case KOBJ_NOTIFY_MODLOADED:
kobj_kdi_mod_state = KOBJ_KDI_MOD_CHANGED;
if (boothowto & RB_DEBUG)
kdi_dvec_mod_loaded(what);
break;
case KOBJ_NOTIFY_MODUNLOADING:
kobj_kdi_mod_state = KOBJ_KDI_MOD_CHANGING;
if (boothowto & RB_DEBUG)
kdi_dvec_mod_unloading(what);
break;
case KOBJ_NOTIFY_MODUNLOADED:
kobj_kdi_mod_state = KOBJ_KDI_MOD_CHANGED;
break;
}
}
/*
* Compare two modctl and module snapshots, attempting to determine whether
* the module to which they both refer has changed between the time of the first
* and the time of the second. We can't do a straight bcmp, because there are
* fields that change in the normal course of operations. False positives
* aren't the end of the world, but it'd be nice to avoid flagging a module
* as changed every time someone holds or releases it.
*/
int
kobj_kdi_mod_haschanged(struct modctl *mc1, struct module *mp1,
struct modctl *mc2, struct module *mp2)
{
if (mc1->mod_loadcnt != mc2->mod_loadcnt || mc1->mod_mp != mc2->mod_mp)
return (1);
if (mc1->mod_mp == NULL)
return (0);
/* Take breath here. */
return (bcmp(&mp1->hdr, &mp2->hdr, sizeof (mp1->hdr)) != 0 ||
mp1->symhdr != mp2->symhdr || mp1->strhdr != mp2->strhdr ||
mp1->text != mp2->text || mp1->bss != mp2->bss ||
mp1->ctfdata != mp2->ctfdata || mp1->ctfsize != mp2->ctfsize);
}
void
kobj_kdi_system_claim(void)
{
kobj_kdi.kdi_plat_call(kobj_kdi.pkdi_system_claim);
kobj_kdi.kdi_plat_call(kobj_kdi.pkdi_console_claim);
}
void
kobj_kdi_system_release(void)
{
kobj_kdi.kdi_plat_call(kobj_kdi.pkdi_console_release);
kobj_kdi.kdi_plat_call(kobj_kdi.pkdi_system_release);
}
void
kobj_kdi_init(void)
{
static const char *const initializers[] = {
"cpu_kdi_init", "mach_kdi_init", "plat_kdi_init", NULL
};
Sym *sym;
int i;
for (i = 0; initializers[i] != NULL; i++) {
if ((sym = kobj_lookup_kernel(initializers[i])) != NULL)
((void (*)(kdi_t *))sym->st_value)(&kobj_kdi);
}
}
kdi_t kobj_kdi = {
KDI_VERSION,
kobj_kdi_mods_changed,
kobj_kdi_mod_iter,
kobj_kdi_mod_isloaded,
kobj_kdi_mod_haschanged,
kobj_kdi_system_claim,
kobj_kdi_system_release,
kdi_pread,
kdi_pwrite,
kdi_flush_caches,
kdi_range_is_nontoxic,
kobj_kdi_get_polled_io,
kdi_vtop,
kdi_dtrace_get_state,
kdi_dtrace_set,
/*
* The rest are filled in by cpu_kdi_init, mach_kdi_init, and/or
* plat_kdi_init.
*/
NULL, /* kdi_plat_call */
NULL, /* kdi_kmdb_enter */
{ NULL }, /* kdi_arch */
{ NULL } /* kdi_plat */
};
|