1/*	$NetBSD: fbt_isa.c,v 1.1 2021/03/29 05:17:09 simonb Exp $	*/
2
3/*
4 * CDDL HEADER START
5 *
6 * The contents of this file are subject to the terms of the
7 * Common Development and Distribution License (the "License").
8 * You may not use this file except in compliance with the License.
9 *
10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 * or http://www.opensolaris.org/os/licensing.
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 *
23 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
24 * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
25 * Portions Copyright 2013 Howard Su howardsu@freebsd.org
26 * Portions Copyright 2015-2016 Ruslan Bukin <br@bsdpad.com>
27 *
28 * $FreeBSD$
29 */
30
31/*
32 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
33 * Use is subject to license terms.
34 */
35
36#include <sys/cdefs.h>
37#include <sys/param.h>
38#include <sys/cpu.h>
39#include <sys/kmem.h>
40#include <sys/module.h>
41
42#include <sys/dtrace.h>
43
44#include <machine/regnum.h>
45#include <machine/locore.h>
46
47#include <mips/cache.h>
48
49#include "fbt.h"
50
51#ifdef __FreeBSD__
52#define	CURRENT_CPU		curcpu
53#endif
54#ifdef __NetBSD__
55#define	CURRENT_CPU		cpu_index(curcpu())
56#endif
57
58#define	FBT_PATCHVAL		(MIPS_BREAK_INSTR)
59#define	FBT_ENTRY		"entry"
60#define	FBT_RETURN		"return"
61
62int
63fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
64{
65	solaris_cpu_t *cpu;
66	fbt_probe_t *fbt;
67
68	cpu = &solaris_cpu[CURRENT_CPU];
69	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
70
71	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
72		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
73			cpu->cpu_dtrace_caller = addr;
74
75			dtrace_probe(fbt->fbtp_id, frame->tf_regs[_R_A0],
76			    frame->tf_regs[_R_A1], frame->tf_regs[_R_A2],
77			    frame->tf_regs[_R_A3], frame->tf_regs[_R_A4]);
78
79			cpu->cpu_dtrace_caller = 0;
80			return (fbt->fbtp_savedval);
81		}
82	}
83
84	return (0);
85}
86
87void
88fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
89{
90
91	*fbt->fbtp_patchpoint = val;
92	mips_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
93}
94
95#if defined(__FreeBSD__)
96int
97fbt_provide_module_function(linker_file_t lf, int symindx,
98    linker_symval_t *symval, void *opaque)
99#elif defined(__NetBSD__)
100int
101fbt_provide_module_cb(const char *name, int symindx, void *value,
102    uint32_t symsize, int type, void *opaque)
103#else
104#error unsupported platform
105#endif
106{
107	fbt_probe_t *fbt, *retfbt;
108	uint32_t *instr, *limit;
109
110#ifdef __FreeBSD__
111	const char *name;
112	char *modname;
113
114	modname = opaque;
115	name = symval->name;
116
117	instr = (uint32_t *)(symval->value);
118	limit = (uint32_t *)(symval->value + symval->size);
119#endif
120#ifdef __NetBSD__
121	struct fbt_ksyms_arg *fka = opaque;
122	modctl_t *mod = fka->fka_mod;
123	const char *modname = module_name(mod);
124
125	/* got a function? */
126	if (ELF_ST_TYPE(type) != STT_FUNC)
127		return 0;
128
129	instr = (uint32_t *)(value);
130	limit = (uint32_t *)((uintptr_t)value + symsize);
131#endif
132
133	/* Check if function is excluded from instrumentation */
134	if (fbt_excluded(name))
135		return (0);
136
137	/* Look for store double to ra register */
138	for (; instr < limit; instr++) {
139		if ((*instr & LDSD_RA_SP_MASK) == SD_RA_SP)
140			break;
141	}
142
143	if (instr >= limit)
144		return (0);
145
146#ifdef __FreeBSD__
147	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
148#endif
149#ifdef __NetBSD__
150	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
151#endif
152	fbt->fbtp_name = name;
153	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
154	    name, FBT_ENTRY, 3, fbt);
155	fbt->fbtp_patchpoint = instr;
156#ifdef __FreeBSD__
157	fbt->fbtp_ctl = lf;
158	fbt->fbtp_loadcnt = lf->loadcnt;
159#endif
160#ifdef __NetBSD__
161	fbt->fbtp_ctl = mod;
162#endif
163	fbt->fbtp_savedval = *instr;
164	fbt->fbtp_patchval = FBT_PATCHVAL;
165	fbt->fbtp_rval = DTRACE_INVOP_SD;
166	fbt->fbtp_symindx = symindx;
167
168	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
169	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
170
171#ifdef __FreeBSD__
172	lf->fbt_nentries++;
173#endif
174
175	retfbt = NULL;
176again:
177	for (; instr < limit; instr++) {
178		if ((*instr & LDSD_RA_SP_MASK) == LD_RA_SP) {
179			break;
180		}
181	}
182
183	if (instr >= limit)
184		return (0);
185
186	/*
187	 * We have a winner!
188	 */
189#ifdef __FreeBSD__
190	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
191#endif
192#ifdef __NetBSD__
193	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
194#endif
195	fbt->fbtp_name = name;
196	if (retfbt == NULL) {
197		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
198		    name, FBT_RETURN, 3, fbt);
199	} else {
200#ifdef __FreeBSD__
201		retfbt->fbtp_probenext = fbt;
202#endif
203#ifdef __NetBSD__
204		fbt->fbtp_ctl = mod;
205#endif
206		fbt->fbtp_id = retfbt->fbtp_id;
207	}
208	retfbt = fbt;
209
210	fbt->fbtp_patchpoint = instr;
211#ifdef __FreeBSD__
212	fbt->fbtp_ctl = lf;
213	fbt->fbtp_loadcnt = lf->loadcnt;
214#endif
215#ifdef __NetBSD__
216	fbt->fbtp_ctl = mod;
217#endif
218	fbt->fbtp_symindx = symindx;
219	fbt->fbtp_rval = DTRACE_INVOP_LD;
220	fbt->fbtp_savedval = *instr;
221	fbt->fbtp_patchval = FBT_PATCHVAL;
222	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
223	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
224
225#ifdef __FreeBSD__
226	lf->fbt_nentries++;
227#endif
228
229	instr++;
230	goto again;
231}
232