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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2018 Joyent, Inc.
26 */
27
28/*
29 * Raw File Target
30 *
31 * The raw file target is invoked whenever a file of unrecognizable type is
32 * specified on the command line, or when raw file examination is forced using
33 * the -f option.  If one file is specified, that file will be opened as the
34 * "object" file.  If two files are specified, the second one will be opened
35 * as the "core" file.  Each file is opened using the fdio backend, which
36 * internally supports both byte-oriented i/o and block-oriented i/o as needed.
37 */
38
39#include <mdb/mdb_modapi.h>
40#include <mdb/mdb_target_impl.h>
41#include <mdb/mdb_io_impl.h>
42#include <mdb/mdb_conf.h>
43#include <mdb/mdb_err.h>
44#include <mdb/mdb.h>
45
46#include <sys/dtrace.h>
47#include <fcntl.h>
48
49typedef struct rf_data {
50	mdb_io_t *r_object_fio;
51	mdb_io_t *r_core_fio;
52} rf_data_t;
53
54#define	RF_OBJECT(p)	(((rf_data_t *)(p))->r_object_fio)
55#define	RF_CORE(p)	(((rf_data_t *)(p))->r_core_fio)
56
57static void
58rf_data_destroy(rf_data_t *rf)
59{
60	if (rf->r_object_fio != NULL)
61		mdb_io_destroy(rf->r_object_fio);
62
63	if (rf->r_core_fio != NULL)
64		mdb_io_destroy(rf->r_core_fio);
65
66	mdb_free(rf, sizeof (rf_data_t));
67}
68
69static int
70rf_setflags(mdb_tgt_t *t, int flags)
71{
72	if ((flags ^ t->t_flags) & MDB_TGT_F_RDWR) {
73		uint_t otflags = t->t_flags;
74		rf_data_t *orf = t->t_data;
75		const char *argv[2];
76		int argc = 0;
77
78		if (orf->r_object_fio != NULL)
79			argv[argc++] = IOP_NAME(orf->r_object_fio);
80		if (orf->r_core_fio != NULL)
81			argv[argc++] = IOP_NAME(orf->r_core_fio);
82
83		t->t_flags = (t->t_flags & ~MDB_TGT_F_RDWR) |
84		    (flags & MDB_TGT_F_RDWR);
85
86		if (mdb_rawfile_tgt_create(t, argc, argv) == -1) {
87			t->t_flags = otflags;
88			t->t_data = orf;
89			return (-1);
90		}
91
92		rf_data_destroy(orf);
93	}
94
95	return (0);
96}
97
98static void
99rf_destroy(mdb_tgt_t *t)
100{
101	rf_data_destroy(t->t_data);
102}
103
104/*ARGSUSED*/
105static const char *
106rf_name(mdb_tgt_t *t)
107{
108	return ("raw");
109}
110
111static ssize_t
112rf_read(mdb_io_t *io, void *buf, size_t nbytes, uint64_t addr)
113{
114	ssize_t rbytes;
115
116	if (io == NULL)
117		return (set_errno(EMDB_NOMAP));
118
119	if (IOP_SEEK(io, addr, SEEK_SET) == -1)
120		return (-1); /* errno is set for us */
121
122	if ((rbytes = IOP_READ(io, buf, nbytes)) == 0)
123		(void) set_errno(EMDB_EOF);
124
125	return (rbytes);
126}
127
128static ssize_t
129rf_write(mdb_io_t *io, const void *buf, size_t nbytes, uint64_t addr)
130{
131	if (io == NULL)
132		return (set_errno(EMDB_NOMAP));
133
134	if (IOP_SEEK(io, addr, SEEK_SET) == -1)
135		return (-1); /* errno is set for us */
136
137	return (IOP_WRITE(io, buf, nbytes));
138}
139
140static ssize_t
141rf_aread(mdb_tgt_t *t, mdb_tgt_as_t as, void *buf,
142    size_t len, mdb_tgt_addr_t addr)
143{
144	switch ((uintptr_t)as) {
145	case (uintptr_t)MDB_TGT_AS_VIRT:
146	case (uintptr_t)MDB_TGT_AS_VIRT_I:
147	case (uintptr_t)MDB_TGT_AS_VIRT_S:
148	case (uintptr_t)MDB_TGT_AS_PHYS:
149		if (RF_CORE(t->t_data) != NULL)
150			return (rf_read(RF_CORE(t->t_data), buf, len, addr));
151		/*FALLTHRU*/
152	case (uintptr_t)MDB_TGT_AS_FILE:
153		return (rf_read(RF_OBJECT(t->t_data), buf, len, addr));
154	default:
155		return (set_errno(EMDB_NOMAP));
156	}
157}
158
159static ssize_t
160rf_awrite(mdb_tgt_t *t, mdb_tgt_as_t as, const void *buf,
161    size_t len, mdb_tgt_addr_t addr)
162{
163	switch ((uintptr_t)as) {
164	case (uintptr_t)MDB_TGT_AS_VIRT:
165	case (uintptr_t)MDB_TGT_AS_VIRT_I:
166	case (uintptr_t)MDB_TGT_AS_VIRT_S:
167	case (uintptr_t)MDB_TGT_AS_PHYS:
168		if (RF_CORE(t->t_data) != NULL)
169			return (rf_write(RF_CORE(t->t_data), buf, len, addr));
170		/*FALLTHRU*/
171	case (uintptr_t)MDB_TGT_AS_FILE:
172		return (rf_write(RF_OBJECT(t->t_data), buf, len, addr));
173	default:
174		return (set_errno(EMDB_NOMAP));
175	}
176}
177
178static ssize_t
179rf_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
180{
181	if (RF_CORE(t->t_data) != NULL)
182		return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
183
184	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
185}
186
187static ssize_t
188rf_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
189{
190	if (RF_CORE(t->t_data) != NULL)
191		return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
192
193	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
194}
195
196static ssize_t
197rf_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr)
198{
199	if (RF_CORE(t->t_data) != NULL)
200		return (rf_read(RF_CORE(t->t_data), buf, nbytes, addr));
201
202	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
203}
204
205static ssize_t
206rf_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr)
207{
208	if (RF_CORE(t->t_data) != NULL)
209		return (rf_write(RF_CORE(t->t_data), buf, nbytes, addr));
210
211	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
212}
213
214static ssize_t
215rf_fread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
216{
217	return (rf_read(RF_OBJECT(t->t_data), buf, nbytes, addr));
218}
219
220static ssize_t
221rf_fwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr)
222{
223	return (rf_write(RF_OBJECT(t->t_data), buf, nbytes, addr));
224}
225
226
227static int
228rf_print_map(mdb_io_t *io, const char *type, int tflags,
229    mdb_tgt_map_f *func, void *private)
230{
231	mdb_map_t map;
232
233	(void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ,
234	    "%s (%s)", IOP_NAME(io), type);
235
236	map.map_base = 0;
237	map.map_size = IOP_SEEK(io, 0, SEEK_END);
238	map.map_flags = MDB_TGT_MAP_R;
239
240	if (tflags & MDB_TGT_F_RDWR)
241		map.map_flags |= MDB_TGT_MAP_W;
242
243	return (func(private, &map, map.map_name));
244}
245
246static int
247rf_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
248{
249	rf_data_t *rf = t->t_data;
250
251	if (rf->r_object_fio != NULL && rf_print_map(rf->r_object_fio,
252	    "object file", t->t_flags, func, private) != 0)
253		return (0);
254
255	if (rf->r_core_fio != NULL && rf_print_map(rf->r_core_fio,
256	    "core file", t->t_flags, func, private) != 0)
257		return (0);
258
259	return (0);
260}
261
262/*ARGSUSED*/
263static int
264rf_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
265{
266	bzero(tsp, sizeof (mdb_tgt_status_t));
267
268	if (RF_CORE(t->t_data) != NULL)
269		tsp->st_state = MDB_TGT_DEAD;
270	else
271		tsp->st_state = MDB_TGT_IDLE;
272
273	return (0);
274}
275
276/*ARGSUSED*/
277static int
278rf_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
279{
280	rf_data_t *rf = mdb.m_target->t_data;
281
282	if (rf->r_object_fio != NULL) {
283		mdb_printf("debugging file '%s' (object file)",
284		    IOP_NAME(rf->r_object_fio));
285
286		if (rf->r_core_fio != NULL) {
287			mdb_printf(" and file '%s' (core file)",
288			    IOP_NAME(rf->r_core_fio));
289		}
290
291		mdb_printf("\n");
292	} else {
293		mdb_printf("debugging empty target\n");
294	}
295
296	return (DCMD_OK);
297}
298
299static const mdb_dcmd_t rf_dcmds[] = {
300	{ "status", NULL, "print summary of current target", rf_status_dcmd },
301	{ NULL }
302};
303
304static const struct rf_magic {
305	const char *rfm_str;
306	size_t rfm_len;
307	const char *rfm_mod;
308} rf_magic[] = {
309	{ DOF_MAG_STRING, DOF_MAG_STRLEN, "dof" },
310	{ NULL, 0, NULL }
311};
312
313static void
314rf_activate(mdb_tgt_t *t)
315{
316	rf_data_t *rf = t->t_data;
317	const struct rf_magic *m;
318	mdb_var_t *v;
319	off64_t size;
320
321	(void) mdb_tgt_register_dcmds(t, &rf_dcmds[0], MDB_MOD_FORCE);
322
323	/*
324	 * We set the legacy adb variable 'd' to be the size of the file (data
325	 * segment).  To get this value, we call seek() on the underlying fdio.
326	 */
327	if (rf->r_object_fio != NULL) {
328		size = IOP_SEEK(rf->r_object_fio, 0, SEEK_END);
329		if ((v = mdb_nv_lookup(&mdb.m_nv, "d")) != NULL)
330			mdb_nv_set_value(v, size);
331	}
332
333	/*
334	 * Load any debugging support modules that match the file type, as
335	 * determined by our poor man's /etc/magic.  If many clients need
336	 * to use this feature, rf_magic[] should be computed dynamically.
337	 */
338	for (m = rf_magic; m->rfm_str != NULL; m++) {
339		char *buf = mdb_alloc(m->rfm_len, UM_SLEEP);
340
341		if (mdb_tgt_vread(t, buf, m->rfm_len, 0) == m->rfm_len &&
342		    bcmp(buf, m->rfm_str, m->rfm_len) == 0) {
343			(void) mdb_module_load(m->rfm_mod,
344			    MDB_MOD_LOCAL | MDB_MOD_SILENT);
345		}
346
347		mdb_free(buf, m->rfm_len);
348	}
349}
350
351static void
352rf_deactivate(mdb_tgt_t *t)
353{
354	const mdb_dcmd_t *dcp;
355
356	for (dcp = &rf_dcmds[0]; dcp->dc_name != NULL; dcp++) {
357		if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1)
358			warn("failed to remove dcmd %s", dcp->dc_name);
359	}
360}
361
362static const mdb_tgt_ops_t rawfile_ops = {
363	rf_setflags,				/* t_setflags */
364	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_setcontext */
365	rf_activate,				/* t_activate */
366	rf_deactivate,				/* t_deactivate */
367	(void (*)())(uintptr_t) mdb_tgt_nop,	/* t_periodic */
368	rf_destroy,				/* t_destroy */
369	rf_name,				/* t_name */
370	(const char *(*)()) mdb_conf_isa,	/* t_isa */
371	(const char *(*)()) mdb_conf_platform,	/* t_platform */
372	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_uname */
373	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_dmodel */
374	rf_aread,				/* t_aread */
375	rf_awrite,				/* t_awrite */
376	rf_vread,				/* t_vread */
377	rf_vwrite,				/* t_vwrite */
378	rf_pread,				/* t_pread */
379	rf_pwrite,				/* t_pwrite */
380	rf_fread,				/* t_fread */
381	rf_fwrite,				/* t_fwrite */
382	(ssize_t (*)()) mdb_tgt_notsup,		/* t_ioread */
383	(ssize_t (*)()) mdb_tgt_notsup,		/* t_iowrite */
384	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_vtop */
385	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_lookup_by_name */
386	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_lookup_by_addr */
387	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_symbol_iter */
388	rf_mapping_iter,			/* t_mapping_iter */
389	rf_mapping_iter,			/* t_object_iter */
390	(const mdb_map_t *(*)()) mdb_tgt_null,	/* t_addr_to_map */
391	(const mdb_map_t *(*)()) mdb_tgt_null,	/* t_name_to_map */
392	(struct ctf_file *(*)()) mdb_tgt_null,	/* t_addr_to_ctf */
393	(struct ctf_file *(*)()) mdb_tgt_null,	/* t_name_to_ctf */
394	rf_status,				/* t_status */
395	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_run */
396	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_step */
397	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_step_out */
398	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_next */
399	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_cont */
400	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_signal */
401	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_vbrkpt */
402	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sbrkpt */
403	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_pwapt */
404	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_vwapt */
405	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_iowapt */
406	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sysenter */
407	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_sysexit */
408	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_signal */
409	(int (*)())(uintptr_t) mdb_tgt_null,	/* t_add_fault */
410	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_getareg */
411	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_putareg */
412	(int (*)())(uintptr_t) mdb_tgt_notsup,	/* t_stack_iter */
413	(int (*)())(uintptr_t) mdb_tgt_notsup	/* t_auxv */
414};
415
416int
417mdb_rawfile_tgt_create(mdb_tgt_t *t, int argc, const char *argv[])
418{
419	mdb_io_t *io[2] = { NULL, NULL };
420	rf_data_t *rf;
421	int oflags, i;
422
423	if (argc > 2)
424		return (set_errno(EINVAL));
425
426	rf = mdb_zalloc(sizeof (rf_data_t), UM_SLEEP);
427	t->t_ops = &rawfile_ops;
428	t->t_data = rf;
429
430	if (t->t_flags & MDB_TGT_F_RDWR)
431		oflags = O_RDWR;
432	else
433		oflags = O_RDONLY;
434
435	for (i = 0; i < argc; i++) {
436		io[i] = mdb_fdio_create_path(NULL, argv[i], oflags, 0);
437		if (io[i] == NULL) {
438			warn("failed to open %s", argv[i]);
439			goto err;
440		}
441	}
442
443	rf->r_object_fio = io[0];	/* first file is the "object" */
444	rf->r_core_fio = io[1];		/* second file is the "core" */
445	t->t_flags |= MDB_TGT_F_ASIO;	/* do i/o using aread and awrite */
446
447	return (0);
448
449err:
450	for (i = 0; i < argc; i++) {
451		if (io[i] != NULL)
452			mdb_io_destroy(io[i]);
453	}
454
455
456	mdb_free(rf, sizeof (rf_data_t));
457	return (set_errno(EMDB_TGT));
458}
459