1299118Sbr/*
2299118Sbr * CDDL HEADER START
3299118Sbr *
4299118Sbr * The contents of this file are subject to the terms of the
5299118Sbr * Common Development and Distribution License (the "License").
6299118Sbr * You may not use this file except in compliance with the License.
7299118Sbr *
8299118Sbr * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9299118Sbr * or http://www.opensolaris.org/os/licensing.
10299118Sbr * See the License for the specific language governing permissions
11299118Sbr * and limitations under the License.
12299118Sbr *
13299118Sbr * When distributing Covered Code, include this CDDL HEADER in each
14299118Sbr * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15299118Sbr * If applicable, add the following below this CDDL HEADER, with the
16299118Sbr * fields enclosed by brackets "[]" replaced with your own identifying
17299118Sbr * information: Portions Copyright [yyyy] [name of copyright owner]
18299118Sbr *
19299118Sbr * CDDL HEADER END
20299118Sbr *
21299118Sbr * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22299118Sbr * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
23299118Sbr * Portions Copyright 2013 Howard Su howardsu@freebsd.org
24299118Sbr * Portions Copyright 2015-2016 Ruslan Bukin <br@bsdpad.com>
25299118Sbr *
26299118Sbr * $FreeBSD: stable/11/sys/cddl/dev/fbt/mips/fbt_isa.c 332566 2018-04-16 14:39:04Z lidl $
27299118Sbr */
28299118Sbr
29299118Sbr/*
30299118Sbr * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
31299118Sbr * Use is subject to license terms.
32299118Sbr */
33299118Sbr
34299118Sbr#include <sys/cdefs.h>
35299118Sbr#include <sys/param.h>
36299118Sbr#include <sys/dtrace.h>
37299118Sbr
38299118Sbr#include <machine/cpuregs.h>
39299118Sbr#include <machine/cache.h>
40299118Sbr
41299118Sbr#include "fbt.h"
42299118Sbr
43299118Sbr#define	FBT_PATCHVAL		(MIPS_BREAK_INSTR)
44299118Sbr#define	FBT_ENTRY		"entry"
45299118Sbr#define	FBT_RETURN		"return"
46299118Sbr
47299118Sbrint
48299118Sbrfbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
49299118Sbr{
50299118Sbr	solaris_cpu_t *cpu;
51299118Sbr	fbt_probe_t *fbt;
52299118Sbr
53299118Sbr	cpu = &solaris_cpu[curcpu];
54299118Sbr	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
55299118Sbr
56299118Sbr	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
57299118Sbr		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
58299118Sbr			cpu->cpu_dtrace_caller = addr;
59299118Sbr
60299118Sbr			dtrace_probe(fbt->fbtp_id, frame->a0,
61299118Sbr			    frame->a1, frame->a2,
62299118Sbr			    frame->a3, frame->a4);
63299118Sbr
64299118Sbr			cpu->cpu_dtrace_caller = 0;
65299118Sbr			return (fbt->fbtp_savedval);
66299118Sbr		}
67299118Sbr	}
68299118Sbr
69299118Sbr	return (0);
70299118Sbr}
71299118Sbr
72299118Sbrvoid
73299118Sbrfbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
74299118Sbr{
75299118Sbr
76299118Sbr	*fbt->fbtp_patchpoint = val;
77299118Sbr	mips_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
78299118Sbr}
79299118Sbr
80299118Sbrint
81299118Sbrfbt_provide_module_function(linker_file_t lf, int symindx,
82299118Sbr    linker_symval_t *symval, void *opaque)
83299118Sbr{
84299118Sbr	fbt_probe_t *fbt, *retfbt;
85299118Sbr	uint32_t *instr, *limit;
86299118Sbr	const char *name;
87299118Sbr	char *modname;
88299118Sbr
89299118Sbr	modname = opaque;
90299118Sbr	name = symval->name;
91299118Sbr
92299118Sbr	/* Check if function is excluded from instrumentation */
93299118Sbr	if (fbt_excluded(name))
94299118Sbr		return (0);
95299118Sbr
96299118Sbr	instr = (uint32_t *)(symval->value);
97299118Sbr	limit = (uint32_t *)(symval->value + symval->size);
98299118Sbr
99299118Sbr	/* Look for store double to ra register */
100299118Sbr	for (; instr < limit; instr++) {
101299118Sbr		if ((*instr & LDSD_RA_SP_MASK) == SD_RA_SP)
102299118Sbr			break;
103299118Sbr	}
104299118Sbr
105299118Sbr	if (instr >= limit)
106299118Sbr		return (0);
107299118Sbr
108299118Sbr	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
109299118Sbr	fbt->fbtp_name = name;
110299118Sbr	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
111299118Sbr	    name, FBT_ENTRY, 3, fbt);
112299118Sbr	fbt->fbtp_patchpoint = instr;
113299118Sbr	fbt->fbtp_ctl = lf;
114299118Sbr	fbt->fbtp_loadcnt = lf->loadcnt;
115299118Sbr	fbt->fbtp_savedval = *instr;
116299118Sbr	fbt->fbtp_patchval = FBT_PATCHVAL;
117299118Sbr	fbt->fbtp_rval = DTRACE_INVOP_SD;
118299118Sbr	fbt->fbtp_symindx = symindx;
119299118Sbr
120299118Sbr	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
121299118Sbr	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
122299118Sbr
123299118Sbr	lf->fbt_nentries++;
124299118Sbr
125299118Sbr	retfbt = NULL;
126299118Sbragain:
127299118Sbr	for (; instr < limit; instr++) {
128299118Sbr		if ((*instr & LDSD_RA_SP_MASK) == LD_RA_SP) {
129299118Sbr			break;
130299118Sbr		}
131299118Sbr	}
132299118Sbr
133299118Sbr	if (instr >= limit)
134299118Sbr		return (0);
135299118Sbr
136299118Sbr	/*
137299118Sbr	 * We have a winner!
138299118Sbr	 */
139299118Sbr	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
140299118Sbr	fbt->fbtp_name = name;
141299118Sbr	if (retfbt == NULL) {
142299118Sbr		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
143299118Sbr		    name, FBT_RETURN, 3, fbt);
144299118Sbr	} else {
145299118Sbr		retfbt->fbtp_next = fbt;
146299118Sbr		fbt->fbtp_id = retfbt->fbtp_id;
147299118Sbr	}
148299118Sbr	retfbt = fbt;
149299118Sbr
150299118Sbr	fbt->fbtp_patchpoint = instr;
151299118Sbr	fbt->fbtp_ctl = lf;
152299118Sbr	fbt->fbtp_loadcnt = lf->loadcnt;
153299118Sbr	fbt->fbtp_symindx = symindx;
154299118Sbr	fbt->fbtp_rval = DTRACE_INVOP_LD;
155299118Sbr	fbt->fbtp_savedval = *instr;
156299118Sbr	fbt->fbtp_patchval = FBT_PATCHVAL;
157299118Sbr	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
158299118Sbr	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
159299118Sbr
160299118Sbr	lf->fbt_nentries++;
161299118Sbr
162299118Sbr	instr++;
163299118Sbr	goto again;
164299118Sbr}
165