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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2016 Joyent, Inc.
26 */
27
28#include <sys/types.h>
29#include <sys/cpr.h>
30#include <sys/promimpl.h>
31#include <sys/privregs.h>
32#include <sys/stack.h>
33#include <sys/bitmap.h>
34#include "cprboot.h"
35
36
37#define	TIMEOUT_MSECS	1000
38
39
40/*
41 * globals
42 */
43int cpr_test_mode;
44csu_md_t mdinfo;
45caddr_t tmp_stack;
46uint_t cb_mid;
47uint_t cb_clock_freq;
48uint_t cpu_delay;
49
50
51/*
52 * file scope
53 */
54typedef void (*tlb_func_t)(int, caddr_t, tte_t *);
55static uint_t mdlen;
56static ulong_t slave_set[BT_BITOUL(NCPU)];
57static int has_scbc;
58
59
60/*
61 * check machdep desc and cpr_machdep info
62 *
63 * sets globals:
64 *	mdinfo
65 *	mdlen
66 */
67int
68cb_check_machdep(void)
69{
70	uint16_t wst32, wst64;
71	char *fmt, *str;
72	cmd_t cmach;
73
74	str = "cb_check_machdep";
75	CB_VPRINTF((ent_fmt, str, entry));
76
77	/*
78	 * get machdep desc and set length of prom words
79	 */
80	SF_DCOPY(cmach);
81	if (cmach.md_magic != CPR_MACHDEP_MAGIC) {
82		prom_printf("%s: bad machdep magic 0x%x, expect 0x%x\n",
83		    str, cmach.md_magic, CPR_MACHDEP_MAGIC);
84		return (ERR);
85	}
86	mdlen = cmach.md_size - sizeof (csu_md_t);
87
88	/*
89	 * get machep info, check for valid stack bias and wstate
90	 */
91	SF_DCOPY(mdinfo);
92	fmt = "found bad statefile data: %s (0x%x), expect 0x%x or 0x%x\n";
93	if (mdinfo.ksb != 0x0 && mdinfo.ksb != V9BIAS64) {
94		prom_printf(fmt, "stack bias", mdinfo.ksb, 0, V9BIAS64);
95		return (ERR);
96	}
97	wst32 = WSTATE(WSTATE_U32, WSTATE_K32);
98	wst64 = WSTATE(WSTATE_U32, WSTATE_K64);
99	if (mdinfo.kwstate != wst32 && mdinfo.kwstate != wst64) {
100		prom_printf(fmt, "wstate", mdinfo.kwstate, wst32, wst64);
101		return (ERR);
102	}
103
104	return (0);
105}
106
107
108/*
109 * interpret saved prom words
110 */
111int
112cb_interpret(void)
113{
114	int bytes, wlen, s;
115	char minibuf[60];
116	char *words;
117
118	CB_VENTRY(cb_interpret);
119
120	/*
121	 * The variable length machdep section for sun4u consists of
122	 * a sequence of null-terminated strings stored contiguously.
123	 *
124	 * The first string defines Forth words which help the prom
125	 * handle kernel translations.
126	 *
127	 * The second string defines Forth words required by kadb to
128	 * interface with the prom when a trap is taken.
129	 */
130	words = SF_DATA();
131	bytes = mdlen;
132	while (bytes) {
133		wlen = prom_strlen(words) + 1;	/* include the null */
134		if (verbose) {
135			s = sizeof (minibuf) - 4;
136			(void) prom_strncpy(minibuf, words, s);
137			if (wlen > s)
138				(void) prom_strcpy(&minibuf[s], "...");
139			prom_printf("    interpret \"%s\"\n", minibuf);
140		}
141		prom_interpret(words, 0, 0, 0, 0, 0);
142		words += wlen;
143		bytes -= wlen;
144	}
145
146	/* advance past prom words */
147	SF_ADV(mdlen);
148
149	return (0);
150}
151
152
153/*
154 * write dtlb/itlb entries
155 */
156static void
157restore_tlb(struct sun4u_tlb *utp, int cpu_id)
158{
159	struct sun4u_tlb *tail;
160	tlb_func_t tfunc;
161	caddr_t virt;
162	char tname;
163
164	if (utp == mdinfo.dtte) {
165		tfunc = set_dtlb_entry;
166		tname = 'd';
167	} else if (utp == mdinfo.itte) {
168		tfunc = set_itlb_entry;
169		tname = 'i';
170	}
171
172	for (tail = utp + CPR_MAX_TLB; utp < tail; utp++) {
173		if (utp->va_tag == NULL)
174			continue;
175		virt = (caddr_t)utp->va_tag;
176		(*tfunc)(utp->index, virt, &utp->tte);
177		if (verbose || CPR_DBG(4)) {
178			prom_printf("    cpu_id %d: write %ctlb "
179			    "(index %x, virt 0x%lx, size 0x%x)\n",
180			    cpu_id, tname, utp->index, utp->va_tag,
181			    TTEBYTES(utp->tte.tte_size));
182		}
183	}
184}
185
186
187/*
188 * install locked tlb entries for the kernel and cpr module;
189 * also sets up the tmp stack
190 */
191int
192cb_ksetup(void)
193{
194	CB_VENTRY(cb_ksetup);
195
196	restore_tlb(mdinfo.dtte, cb_mid);
197	restore_tlb(mdinfo.itte, cb_mid);
198	tmp_stack = (caddr_t)(mdinfo.tmp_stack + mdinfo.tmp_stacksize);
199
200	return (0);
201}
202
203
204static void
205cb_park_err(int cpu_id)
206{
207	prom_printf("\ncpu_id %d did not stop!...\n", cpu_id);
208	cb_exit_to_mon();
209}
210
211
212/*
213 * local copy of an older interface for OBP revs < 4.6
214 */
215static int
216cb_prom_stop_self(void)
217{
218	cell_t ci[3];
219
220	ci[0] = p1275_ptr2cell("SUNW,stop-self");	/* Service name */
221	ci[1] = (cell_t)0;			/* #argument cells */
222	ci[2] = (cell_t)0;			/* #result cells */
223	(void) p1275_cif_handler(&ci);		/* Do NOT lock */
224	return (0);
225}
226
227
228/*
229 * install locked tlb entries and spin or park in a prom idle-loop
230 */
231void
232slave_init(int cpu_id)
233{
234	restore_tlb(mdinfo.dtte, cpu_id);
235	restore_tlb(mdinfo.itte, cpu_id);
236	BT_SET(slave_set, cpu_id);
237	membar_stld();
238	if (has_scbc) {
239		/* just spin, master will park this cpu */
240		/* CONSTCOND */
241		while (1);
242	} else {
243		(void) cb_prom_stop_self();
244		cb_park_err(cpu_id);
245	}
246}
247
248
249/*
250 * when any cpu is started, they naturally rely on the prom for all
251 * text/data translations until switching to the kernel trap table.
252 * to jump back into the cpr module and to restart slave cpus, cprboot
253 * needs to reinstall translations for the nucleus and some cpr pages.
254 *
255 * the easy method is creating one set of global translations available
256 * to all cpus with prom_map(); unfortunately, a 4MB "map" request will
257 * allocate and overwrite a few pages, and these are often kernel pages
258 * that were just restored.
259 *
260 * to solve the "map" problem, all cpus install their own set of locked
261 * tlb entries to translate the nucleus and parts of the cpr module;
262 * after all cpus have switched to kernel traps, any of these locked
263 * tlb entries for pages outside the nucleus will be cleared.
264 */
265int
266cb_mpsetup(void)
267{
268	struct sun4u_cpu_info *scip, *tail;
269	int timeout, ncpu;
270	char *str, *intf;
271
272	intf = "SUNW,stop-cpu-by-cpuid";
273	has_scbc = (prom_test(intf) == 0);
274	CB_VPRINTF(("\n\"%s\" test %d\n", intf, has_scbc));
275
276	str = "cb_mp_setup";
277	CB_VPRINTF((ent_fmt, str, entry));
278
279	/*
280	 * launch any slave cpus from the .sci array into cprboot text
281	 * and wait about a second for them to checkin with slave_set
282	 */
283	ncpu = 0;
284	bzero(slave_set, sizeof (slave_set));
285	for (scip = mdinfo.sci, tail = scip + NCPU; scip < tail; scip++) {
286		if (scip->node == 0 || scip->cpu_id == cb_mid)
287			continue;
288		(void) prom_startcpu(scip->node,
289		    (caddr_t)cpu_launch, scip->cpu_id);
290
291		for (timeout = TIMEOUT_MSECS; timeout; timeout--) {
292			if (BT_TEST(slave_set, scip->cpu_id))
293				break;
294			cb_usec_wait(MILLISEC);
295		}
296
297		if (timeout == 0) {
298			prom_printf("\n%s: cpu did not start, "
299			    "cpu_id %d, node 0x%x\n",
300			    prog, scip->cpu_id, scip->node);
301			return (ERR);
302		}
303
304		if (has_scbc && prom_stopcpu_bycpuid(scip->cpu_id))
305			cb_park_err(scip->cpu_id);
306
307		ncpu++;
308	}
309
310	if (verbose && ncpu)
311		prom_printf("\n%s: slave cpu count: %d\n", str, ncpu);
312
313	return (0);
314}
315