xref: /illumos-gate/usr/src/uts/sun4v/os/mach_mp_startup.c (revision e0731422366620894c16c1ee6515551c5f00733d)
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/machsystm.h>
27 #include <sys/cpu_module.h>
28 #include <sys/dtrace.h>
29 #include <sys/cpu_sgnblk_defs.h>
30 #include <sys/mach_descrip.h>
31 #include <sys/ldoms.h>
32 #include <sys/hypervisor_api.h>
33 #include <sys/soft_state.h>
34 #include <sys/mpo.h>
35 
36 /*
37  * Useful for disabling MP bring-up for an MP capable kernel
38  * (a kernel that was built with MP defined)
39  */
40 int use_mp = 1;			/* set to come up mp */
41 
42 /*
43  * Init CPU info - get CPU type info for processor_info system call.
44  */
45 void
46 init_cpu_info(struct cpu *cp)
47 {
48 	processor_info_t *pi = &cp->cpu_type_info;
49 	int cpuid = cp->cpu_id;
50 	struct cpu_node *cpunode = &cpunodes[cpuid];
51 
52 	cp->cpu_fpowner = NULL;		/* not used for V9 */
53 
54 	/*
55 	 * Get clock-frequency property from cpunodes[] for the CPU.
56 	 */
57 	pi->pi_clock = (cpunode->clock_freq + 500000) / 1000000;
58 
59 	/*
60 	 * Current frequency in Hz.
61 	 */
62 	cp->cpu_curr_clock = cpunode->clock_freq;
63 
64 	/*
65 	 * Supported frequencies.
66 	 */
67 	cpu_set_supp_freqs(cp, NULL);
68 
69 	(void) strcpy(pi->pi_processor_type, "sparcv9");
70 	(void) strcpy(pi->pi_fputypes, "sparcv9");
71 
72 	/*
73 	 * StarFire requires the signature block stuff setup here
74 	 */
75 	CPU_SGN_MAPIN(cpuid);
76 
77 	/*
78 	 * cpu0 is always initialized at boot time, but it can be initialized
79 	 * again if it is dynamically removed and then re-added. We check if
80 	 * we are booting by verifying cpu_list. During boot, cpu0 is already
81 	 * in cpu_list when this function is called. When a cpu is dynamically
82 	 * added (after the boot) then it is added to cpu_list after this
83 	 * function is called.
84 	 */
85 	if (cpuid == cpu0.cpu_id && ncpus == 1 && cpu_list[0].cpu_id == cpuid) {
86 		/*
87 		 * cpu0 starts out running.  Other cpus are
88 		 * still in OBP land and we will leave them
89 		 * alone for now.
90 		 */
91 		CPU_SIGNATURE(OS_SIG, SIGST_RUN, SIGSUBST_NULL, cpuid);
92 		/*
93 		 * On first cpu setup, tell hv we are booting
94 		 */
95 		mach_set_soft_state(SIS_TRANSITION,
96 		    &SOLARIS_SOFT_STATE_BOOT_MSG);
97 #ifdef	lint
98 		cpuid = cpuid;
99 #endif	/* lint */
100 	}
101 }
102 
103 /*
104  * Routine used to cleanup a CPU that has been powered off. This will
105  * destroy all per-cpu information related to this cpu.
106  */
107 int
108 mp_cpu_unconfigure(int cpuid)
109 {
110 	int retval;
111 	extern void empty_cpu(int);
112 	extern int cleanup_cpu_common(int);
113 
114 	ASSERT(MUTEX_HELD(&cpu_lock));
115 
116 	retval = cleanup_cpu_common(cpuid);
117 
118 	empty_cpu(cpuid);
119 
120 	mpo_cpu_remove(cpuid);
121 
122 	return (retval);
123 }
124 
125 struct mp_find_cpu_arg {
126 	int cpuid;		/* set by mp_cpu_configure() */
127 	dev_info_t *dip;	/* set by mp_find_cpu() */
128 };
129 
130 int
131 mp_find_cpu(dev_info_t *dip, void *arg)
132 {
133 	struct mp_find_cpu_arg *target = (struct mp_find_cpu_arg *)arg;
134 	char	*type;
135 	int	rv = DDI_WALK_CONTINUE;
136 	int	cpuid;
137 
138 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
139 	    DDI_PROP_DONTPASS, "device_type", &type))
140 		return (DDI_WALK_CONTINUE);
141 
142 	if (strcmp(type, "cpu") != 0)
143 		goto out;
144 
145 	cpuid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
146 	    DDI_PROP_DONTPASS, "reg", -1);
147 
148 	if (cpuid == -1) {
149 		cmn_err(CE_PANIC, "reg prop not found in cpu node");
150 	}
151 
152 	cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
153 
154 	if (cpuid != target->cpuid)
155 		goto out;
156 
157 	/* Found it */
158 	rv = DDI_WALK_TERMINATE;
159 	target->dip = dip;
160 
161 out:
162 	ddi_prop_free(type);
163 	return (rv);
164 }
165 
166 /*
167  * Routine used to setup a newly inserted CPU in preparation for starting
168  * it running code.
169  */
170 int
171 mp_cpu_configure(int cpuid)
172 {
173 	md_t		*mdp;
174 	mde_cookie_t	rootnode, cpunode = MDE_INVAL_ELEM_COOKIE;
175 	int		listsz, i;
176 	mde_cookie_t	*listp = NULL;
177 	int		num_nodes;
178 	uint64_t	cpuid_prop;
179 	cpu_t		*cpu;
180 	processorid_t	id;
181 
182 	ASSERT(MUTEX_HELD(&cpu_lock));
183 
184 	if ((mdp = md_get_handle()) == NULL)
185 		return (ENODEV);
186 
187 	rootnode = md_root_node(mdp);
188 
189 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
190 
191 	num_nodes = md_node_count(mdp);
192 
193 	ASSERT(num_nodes > 0);
194 
195 	listsz = num_nodes * sizeof (mde_cookie_t);
196 	listp = kmem_zalloc(listsz, KM_SLEEP);
197 
198 	num_nodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
199 	    md_find_name(mdp, "fwd"), listp);
200 
201 	if (num_nodes < 0)
202 		return (ENODEV);
203 
204 	for (i = 0; i < num_nodes; i++) {
205 		if (md_get_prop_val(mdp, listp[i], "id", &cpuid_prop))
206 			break;
207 		if (cpuid_prop == (uint64_t)cpuid) {
208 			cpunode = listp[i];
209 			break;
210 		}
211 	}
212 
213 	if (cpunode == MDE_INVAL_ELEM_COOKIE)
214 		return (ENODEV);
215 
216 	kmem_free(listp, listsz);
217 
218 	mpo_cpu_add(mdp, cpuid);
219 
220 	/*
221 	 * Note: uses cpu_lock to protect cpunodes
222 	 * which will be modified inside of fill_cpu and
223 	 * setup_exec_unit_mappings.
224 	 */
225 	fill_cpu(mdp, cpunode);
226 
227 	/*
228 	 * Adding a CPU may cause the execution unit sharing
229 	 * relationships to change. Update the mappings in
230 	 * the cpunode structures.
231 	 */
232 	setup_chip_mappings(mdp);
233 	setup_exec_unit_mappings(mdp);
234 
235 	/* propagate the updated mappings to the CPU structures */
236 	for (id = 0; id < NCPU; id++) {
237 		if ((cpu = cpu_get(id)) == NULL)
238 			continue;
239 
240 		cpu_map_exec_units(cpu);
241 	}
242 
243 	(void) md_fini_handle(mdp);
244 
245 	if ((i = setup_cpu_common(cpuid)) != 0) {
246 		(void) cleanup_cpu_common(cpuid);
247 		return (i);
248 	}
249 
250 	return (0);
251 }
252 
253 /*
254  * Platform-specific actions to be taken when all cpus are running
255  * in the OS.
256  */
257 void
258 cpu_mp_init(void)
259 {
260 	extern void recalc_xc_timeouts();
261 	extern int cif_cpu_mp_ready;
262 
263 	/* N.B. This must happen after xc_init() has run. */
264 	recalc_xc_timeouts();
265 
266 	if (!domaining_enabled())
267 		return;
268 
269 	cif_cpu_mp_ready = 1;
270 }
271 
272 void
273 populate_idstr(struct cpu *cp)
274 {
275 	char buf[CPU_IDSTRLEN];
276 	struct cpu_node *cpunode;
277 	processor_info_t *pi;
278 
279 	cpunode = &cpunodes[cp->cpu_id];
280 	pi = &cp->cpu_type_info;
281 	if (cp->cpu_m.cpu_chip == CPU_CHIPID_INVALID) {
282 		(void) snprintf(buf, sizeof (buf),
283 		    "%s (cpuid %d, clock %d MHz)",
284 		    cpunode->name, cpunode->cpuid, pi->pi_clock);
285 	} else {
286 		(void) snprintf(buf, sizeof (buf),
287 		    "%s (chipid %d, clock %d MHz)",
288 		    cpunode->name, cp->cpu_m.cpu_chip, pi->pi_clock);
289 	}
290 
291 	cp->cpu_idstr = kmem_alloc(strlen(buf) + 1, KM_SLEEP);
292 	(void) strcpy(cp->cpu_idstr, buf);
293 
294 	cp->cpu_brandstr = kmem_alloc(strlen(cpunode->name) + 1, KM_SLEEP);
295 	(void) strcpy(cp->cpu_brandstr, cpunode->name);
296 
297 	cmn_err(CE_CONT, "?cpu%d: %s\n", cp->cpu_id, cp->cpu_idstr);
298 }
299