xref: /illumos-gate/usr/src/uts/sun4v/promif/promif_emul.c (revision 4cb70d33a0347eb8a72054ff37d58fb4b7fb185f)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/promif_impl.h>
30 #include <sys/machsystm.h>
31 #include <sys/lpad.h>
32 #include <sys/vmsystm.h>
33 #include <sys/prom_plat.h>
34 #include <sys/ldoms.h>
35 #include <sys/kobj.h>
36 #include <sys/reboot.h>
37 #include <sys/hypervisor_api.h>
38 #include <sys/mdesc.h>
39 #include <sys/mach_descrip.h>
40 
41 #ifndef _KMDB
42 static processorid_t cif_cpu;
43 static struct translation *cif_prom_trans;
44 static size_t cif_prom_ntrans;
45 
46 int cif_cpu_mp_ready;
47 int (*prom_cif_handler)(void *) = NULL;
48 #endif
49 
50 #ifdef DEBUG
51 uint_t cif_debug;
52 #endif /* DEBUG */
53 
54 extern int (*cif_handler)(void *);
55 
56 typedef struct {
57 	char		*name;
58 	cif_func_t	func;
59 } cif_callback_t;
60 
61 static cif_callback_t cb_table[] = {
62 	{ "getprop",			promif_getprop		    },
63 	{ "getproplen",			promif_getproplen	    },
64 	{ "nextprop",			promif_nextprop		    },
65 	{ "peer",			promif_nextnode		    },
66 	{ "child",			promif_childnode	    },
67 	{ "parent",			promif_parentnode	    },
68 	{ "enter",			promif_enter_mon	    },
69 	{ "exit",			promif_exit_to_mon	    },
70 	{ "boot",			promif_reboot		    },
71 	{ "write",			promif_write		    },
72 	{ "read",			promif_read		    },
73 	{ "interpret",			promif_interpret	    },
74 	{ "finddevice",			promif_finddevice	    },
75 	{ "instance-to-package",	promif_instance_to_package  },
76 #ifndef _KMDB
77 	{ "setprop",			promif_setprop		    },
78 	{ "test",			promif_test		    },
79 	{ "instance-to-path",		promif_instance_to_path	    },
80 	{ "SUNW,power-off",		promif_power_off	    },
81 	{ "SUNW,asr-list-keys-len",	promif_asr_list_keys_len    },
82 	{ "SUNW,asr-list-keys",		promif_asr_list_keys	    },
83 	{ "SUNW,asr-export-len",	promif_asr_export_len	    },
84 	{ "SUNW,asr-export",		promif_asr_export	    },
85 	{ "SUNW,set-security-key",	promif_set_security_key	    },
86 	{ "SUNW,get-security-key",	promif_get_security_key	    },
87 	{ "SUNW,start-cpu-by-cpuid",	promif_start_cpu	    },
88 	{ "SUNW,set-trap-table",	promif_set_mmfsa_traptable  },
89 	{ "SUNW,set-sun4v-api-version",	promif_set_sun4v_api_version },
90 	{ "SUNW,get-sun4v-api-version",	promif_get_sun4v_api_version },
91 #endif
92 	{ NULL,				NULL			    }
93 };
94 
95 cif_func_t
96 promif_find_cif_callback(char *opname)
97 {
98 	cif_callback_t	*cb;
99 
100 	if (opname == NULL)
101 		return (NULL);
102 
103 	for (cb = cb_table; cb->name; cb++) {
104 		if (prom_strcmp(cb->name, opname) == 0)
105 			break;
106 	}
107 
108 	return (cb->func);
109 }
110 
111 static int
112 kern_cif_handler(void *p)
113 {
114 	cell_t		*ci = (cell_t *)p;
115 	char		*opname;
116 	cif_func_t	func;
117 	int		rv;
118 
119 	ASSERT(cif_handler == kern_cif_handler);
120 
121 #ifndef _KMDB
122 	cif_cpu = getprocessorid();
123 #endif
124 
125 	opname = p1275_cell2ptr(ci[0]);
126 
127 	/* lookup the callback for the desired operation */
128 	func = promif_find_cif_callback(opname);
129 
130 	if (func == NULL) {
131 #ifdef _KMDB
132 		prom_fatal_error("sun4v unsupported CIFs\n");
133 #else
134 		cmn_err(CE_CONT, "!sun4v unsupported CIF: %s\n", opname);
135 		return (-1);
136 #endif
137 	}
138 
139 	/* callback found, execute it */
140 	rv = func(p);
141 
142 #ifndef _KMDB
143 	cif_cpu = -1;
144 #endif
145 
146 	return (rv);
147 }
148 
149 #ifdef _KMDB
150 
151 void
152 cif_init(char *pgmname, caddr_t root, ihandle_t in, ihandle_t out,
153     phandle_t pin, phandle_t pout, pnode_t chosen, pnode_t options)
154 {
155 	/* initialize pointer to a copy of OBP device tree */
156 	promif_stree_setroot(root);
157 
158 	promif_set_nodes(chosen, options);
159 
160 	/* initialize io parameters */
161 	promif_io_init(in, out, pin, pout);
162 
163 	/*
164 	 * Switch CIF handler to the kernel.
165 	 */
166 	if (pgmname != NULL)
167 		prom_init(pgmname, (void *)kern_cif_handler);
168 	else
169 		cif_handler = kern_cif_handler;
170 }
171 
172 #else
173 
174 static void cache_prom_data(void);
175 
176 /*
177  * This function returns 1 if the current thread is executing in
178  * the CIF and 0 otherwise. This is useful information to know
179  * since code that implements CIF handlers can assume that it has
180  * gone through the kern_preprom() entry point, implying it is
181  * running single threaded, has preemption disabled, etc.
182  */
183 int
184 promif_in_cif(void)
185 {
186 	int	mycpuid = getprocessorid();
187 
188 	return ((cif_cpu == mycpuid) ? 1 : 0);
189 }
190 
191 /*
192  * Check that all cpus in the MD are within range (< NCPU).  Attempt
193  * to stop any that aren't.
194  */
195 static void
196 cif_check_cpus(void)
197 {
198 	md_t		*mdp;
199 	mde_cookie_t	rootnode;
200 	size_t		listsz;
201 	int		i;
202 	mde_cookie_t	*listp = NULL;
203 	int		num_nodes;
204 	uint64_t	cpuid;
205 	int		status;
206 
207 	mdp = md_get_handle();
208 	ASSERT(mdp);
209 
210 	rootnode = md_root_node(mdp);
211 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
212 
213 	num_nodes = md_node_count(mdp);
214 	ASSERT(num_nodes > 0);
215 
216 	listsz = num_nodes * sizeof (mde_cookie_t);
217 	listp = kmem_zalloc(listsz, KM_SLEEP);
218 
219 	num_nodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
220 	    md_find_name(mdp, "fwd"), listp);
221 
222 	if (num_nodes <= 0)
223 		goto done;
224 
225 	for (i = 0; i < num_nodes; i++) {
226 		if (md_get_prop_val(mdp, listp[i], "id", &cpuid)) {
227 			cmn_err(CE_WARN, "cif_check_cpus: "
228 			    "CPU instance %d has no 'id' property", i);
229 			continue;
230 		}
231 
232 		mutex_enter(&cpu_lock);
233 
234 		if (cpuid >= NCPU) {
235 			status = stopcpu_bycpuid(cpuid);
236 			if (status != 0 && status != ENOTSUP)
237 				cmn_err(CE_PANIC, "failed to stop cpu %lu (%d)",
238 				    cpuid, status);
239 		}
240 
241 		mutex_exit(&cpu_lock);
242 	}
243 
244 done:
245 	kmem_free(listp, listsz);
246 }
247 
248 void
249 cif_init(void)
250 {
251 	void (*kmdb_cb)(void);
252 	uint64_t rtba;
253 	uint64_t rv;
254 
255 	/*
256 	 * Check if domaining is enabled. If not, do not
257 	 * initialize the kernel CIF handler.
258 	 */
259 	if (!(domaining_capabilities & DOMAINING_ENABLED))
260 		return;
261 
262 	/*
263 	 * Cache PROM data that is needed later, e.g. a shadow
264 	 * copy of the device tree, IO mappings, etc.
265 	 */
266 	cache_prom_data();
267 
268 	/*
269 	 * Prepare to take over the get/set of environmental variables.
270 	 */
271 	promif_prop_init();
272 
273 	/*
274 	 * Switch CIF handler to the kernel.
275 	 */
276 	prom_cif_handler = cif_handler;
277 
278 	promif_preprom();
279 	cif_handler = kern_cif_handler;
280 
281 	/*
282 	 * Take over rtba for the boot CPU. The rtba for
283 	 * all other CPUs are set as they enter the system.
284 	 */
285 	rtba = va_to_pa(&trap_table);
286 	if ((rv = hv_cpu_set_rtba(&rtba)) != H_EOK)
287 		panic("hv_cpu_set_rtba failed: %ld\n", rv);
288 
289 	promif_postprom();
290 
291 	/*
292 	 * If the system has been booted with kmdb we need kmdb to
293 	 * use the kernel cif handler instead of the PROM cif handler.
294 	 */
295 	if (boothowto & RB_KMDB) {
296 		kmdb_cb = (void (*)(void))modlookup("misc/kmdbmod",
297 		    "kctl_switch_promif");
298 		ASSERT(kmdb_cb != NULL);
299 		(*kmdb_cb)();
300 	}
301 
302 	cif_check_cpus();
303 }
304 
305 static void
306 cache_prom_data(void)
307 {
308 	/* initialize copy of OBP device tree */
309 	promif_stree_init();
310 
311 	/* initialize io parameters */
312 	promif_io_init();
313 }
314 
315 
316 /*
317  * Platform-specific actions to be taken when all cpus are running
318  * in the OS.
319  */
320 void
321 cpu_mp_init(void)
322 {
323 	if (!(domaining_capabilities & DOMAINING_ENABLED))
324 		return;
325 
326 	cif_cpu_mp_ready = 1;
327 }
328 
329 #endif	/* _KMDB */
330