xref: /illumos-gate/usr/src/uts/sun4v/os/mach_startup.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
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  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/machsystm.h>
30 #include <sys/archsystm.h>
31 #include <sys/prom_plat.h>
32 #include <sys/promif.h>
33 #include <sys/vm.h>
34 #include <sys/cpu.h>
35 #include <sys/atomic.h>
36 #include <sys/cpupart.h>
37 #include <sys/disp.h>
38 #include <sys/hypervisor_api.h>
39 #include <sys/traptrace.h>
40 
41 #ifdef TRAPTRACE
42 int mach_htraptrace_enable = 1;
43 #else
44 int mach_htraptrace_enable = 0;
45 #endif
46 int htrap_tr0_inuse = 0;
47 caddr_t httrace_buf;	/* hv traptrace buffer for all cpus except 0 */
48 extern char htrap_tr0[];	/* prealloc buf for boot cpu */
49 
50 caddr_t	mmu_fault_status_area;
51 
52 extern void sfmmu_set_tsbs(void);
53 /*
54  * CPU IDLE optimization variables/routines
55  */
56 static int enable_halt_idle_cpus = 1;
57 
58 void
59 setup_trap_table(void)
60 {
61 	caddr_t mmfsa_va;
62 	extern	 caddr_t mmu_fault_status_area;
63 	mmfsa_va =
64 	    mmu_fault_status_area + (MMFSA_SIZE * CPU->cpu_id);
65 
66 	intr_init(CPU);			/* init interrupt request free list */
67 	setwstate(WSTATE_KERN);
68 	set_mmfsa_scratchpad(mmfsa_va);
69 	prom_set_mmfsa_traptable(&trap_table, va_to_pa(mmfsa_va));
70 	sfmmu_set_tsbs();
71 }
72 
73 void
74 phys_install_has_changed(void)
75 {
76 
77 }
78 
79 /*
80  * Halt the present CPU until awoken via an interrupt
81  */
82 static void
83 cpu_halt(void)
84 {
85 	cpu_t *cpup = CPU;
86 	processorid_t cpun = cpup->cpu_id;
87 	cpupart_t *cp = cpup->cpu_part;
88 	int hset_update = 1;
89 	uint_t s;
90 
91 	/*
92 	 * If this CPU is online, and there's multiple CPUs
93 	 * in the system, then we should notate our halting
94 	 * by adding ourselves to the partition's halted CPU
95 	 * bitmap. This allows other CPUs to find/awaken us when
96 	 * work becomes available.
97 	 */
98 	if (CPU->cpu_flags & CPU_OFFLINE || ncpus == 1)
99 		hset_update = 0;
100 
101 	/*
102 	 * Add ourselves to the partition's halted CPUs bitmask
103 	 * and set our HALTED flag, if necessary.
104 	 *
105 	 * When a thread becomes runnable, it is placed on the queue
106 	 * and then the halted cpuset is checked to determine who
107 	 * (if anyone) should be awoken. We therefore need to first
108 	 * add ourselves to the halted cpuset, and then check if there
109 	 * is any work available.
110 	 */
111 	if (hset_update) {
112 		cpup->cpu_disp_flags |= CPU_DISP_HALTED;
113 		membar_producer();
114 		CPUSET_ATOMIC_ADD(cp->cp_haltset, cpun);
115 	}
116 
117 	/*
118 	 * Check to make sure there's really nothing to do.
119 	 * Work destined for this CPU may become available after
120 	 * this check. We'll be notified through the clearing of our
121 	 * bit in the halted CPU bitmask, and a poke.
122 	 */
123 	if (disp_anywork()) {
124 		if (hset_update) {
125 			cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
126 			CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
127 		}
128 		return;
129 	}
130 
131 	/*
132 	 * We're on our way to being halted.
133 	 *
134 	 * Disable interrupts now, so that we'll awaken immediately
135 	 * after halting if someone tries to poke us between now and
136 	 * the time we actually halt.
137 	 *
138 	 * We check for the presence of our bit after disabling interrupts.
139 	 * If it's cleared, we'll return. If the bit is cleared after
140 	 * we check then the poke will pop us out of the halted state.
141 	 *
142 	 * The ordering of the poke and the clearing of the bit by cpu_wakeup
143 	 * is important.
144 	 * cpu_wakeup() must clear, then poke.
145 	 * cpu_halt() must disable interrupts, then check for the bit.
146 	 */
147 	s = disable_vec_intr();
148 
149 	if (hset_update && !CPU_IN_SET(cp->cp_haltset, cpun)) {
150 		cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
151 		enable_vec_intr(s);
152 		return;
153 	}
154 
155 	/*
156 	 * The check for anything locally runnable is here for performance
157 	 * and isn't needed for correctness. disp_nrunnable ought to be
158 	 * in our cache still, so it's inexpensive to check, and if there
159 	 * is anything runnable we won't have to wait for the poke.
160 	 */
161 	if (cpup->cpu_disp->disp_nrunnable != 0) {
162 		if (hset_update) {
163 			cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
164 			CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
165 		}
166 		enable_vec_intr(s);
167 		return;
168 	}
169 
170 	/*
171 	 * Halt the strand
172 	 */
173 	(void) hv_cpu_yield();
174 
175 	/*
176 	 * We're no longer halted
177 	 */
178 	enable_vec_intr(s);
179 	if (hset_update) {
180 		cpup->cpu_disp_flags &= ~CPU_DISP_HALTED;
181 		CPUSET_ATOMIC_DEL(cp->cp_haltset, cpun);
182 	}
183 }
184 
185 /*
186  * If "cpu" is halted, then wake it up clearing its halted bit in advance.
187  * Otherwise, see if other CPUs in the cpu partition are halted and need to
188  * be woken up so that they can steal the thread we placed on this CPU.
189  * This function is only used on MP systems.
190  */
191 static void
192 cpu_wakeup(cpu_t *cpu, int bound)
193 {
194 	uint_t		cpu_found;
195 	int		result;
196 	cpupart_t	*cp;
197 
198 	cp = cpu->cpu_part;
199 	if (CPU_IN_SET(cp->cp_haltset, cpu->cpu_id)) {
200 		/*
201 		 * Clear the halted bit for that CPU since it will be
202 		 * poked in a moment.
203 		 */
204 		CPUSET_ATOMIC_DEL(cp->cp_haltset, cpu->cpu_id);
205 		/*
206 		 * We may find the current CPU present in the halted cpuset
207 		 * if we're in the context of an interrupt that occurred
208 		 * before we had a chance to clear our bit in cpu_halt().
209 		 * Poking ourself is obviously unnecessary, since if
210 		 * we're here, we're not halted.
211 		 */
212 		if (cpu != CPU)
213 			poke_cpu(cpu->cpu_id);
214 		return;
215 	} else {
216 		/*
217 		 * This cpu isn't halted, but it's idle or undergoing a
218 		 * context switch. No need to awaken anyone else.
219 		 */
220 		if (cpu->cpu_thread == cpu->cpu_idle_thread ||
221 		    cpu->cpu_disp_flags & CPU_DISP_DONTSTEAL)
222 			return;
223 	}
224 
225 	/*
226 	 * No need to wake up other CPUs if the thread we just enqueued
227 	 * is bound.
228 	 */
229 	if (bound)
230 		return;
231 
232 	/*
233 	 * See if there's any other halted CPUs. If there are, then
234 	 * select one, and awaken it.
235 	 * It's possible that after we find a CPU, somebody else
236 	 * will awaken it before we get the chance.
237 	 * In that case, look again.
238 	 */
239 	do {
240 		CPUSET_FIND(cp->cp_haltset, cpu_found);
241 		if (cpu_found == CPUSET_NOTINSET)
242 			return;
243 
244 		ASSERT(cpu_found >= 0 && cpu_found < NCPU);
245 		CPUSET_ATOMIC_XDEL(cp->cp_haltset, cpu_found, result);
246 	} while (result < 0);
247 
248 	if (cpu_found != CPU->cpu_id)
249 		poke_cpu(cpu_found);
250 }
251 
252 void
253 mach_cpu_halt_idle()
254 {
255 	if (enable_halt_idle_cpus) {
256 		idle_cpu = cpu_halt;
257 		disp_enq_thread = cpu_wakeup;
258 	}
259 }
260 
261 int
262 ndata_alloc_mmfsa(struct memlist *ndata)
263 {
264 	size_t	size;
265 
266 	size = MMFSA_SIZE * max_ncpus;
267 	mmu_fault_status_area = ndata_alloc(ndata, size, ecache_alignsize);
268 	if (mmu_fault_status_area == NULL)
269 		return (-1);
270 	return (0);
271 }
272 
273 void
274 mach_memscrub(void)
275 {
276 	/* no memscrub support for sun4v for now */
277 }
278 
279 void
280 mach_fpras()
281 {
282 	/* no fpras support for sun4v for now */
283 }
284 
285 void
286 mach_hw_copy_limit(void)
287 {
288 	/* HW copy limits set by individual CPU module */
289 }
290 
291 /*
292  * We need to enable soft ring functionality on Niagara platform since
293  * one strand can't handle interrupts for a 1Gb NIC. Set the tunable
294  * ip_squeue_soft_ring by default on this platform. We can also set
295  * ip_threads_per_cpu to track number of threads per core. The variables
296  * themselves are defined in space.c and used by IP module
297  */
298 extern uint_t ip_threads_per_cpu;
299 extern boolean_t ip_squeue_soft_ring;
300 void
301 startup_platform(void)
302 {
303 	ip_squeue_soft_ring = B_TRUE;
304 }
305 
306 /*
307  * This function sets up hypervisor traptrace buffer
308  * This routine is called by the boot cpu only
309  */
310 void
311 mach_htraptrace_setup(int cpuid)
312 {
313 	TRAP_TRACE_CTL	*ctlp;
314 	int bootcpuid = getprocessorid(); /* invoked on boot cpu only */
315 
316 	if (mach_htraptrace_enable && ((cpuid != bootcpuid) ||
317 	    !htrap_tr0_inuse)) {
318 		ctlp = &trap_trace_ctl[cpuid];
319 		ctlp->d.hvaddr_base = (cpuid == bootcpuid) ? htrap_tr0 :
320 		    httrace_buf + (cpuid * HTRAP_TSIZE);
321 		ctlp->d.hlimit = HTRAP_TSIZE;
322 		ctlp->d.hpaddr_base = va_to_pa(ctlp->d.hvaddr_base);
323 	}
324 }
325 
326 /*
327  * This function enables or disables the hypervisor traptracing
328  */
329 void
330 mach_htraptrace_configure(int cpuid)
331 {
332 	uint64_t ret;
333 	uint64_t prev_buf, prev_bufsize;
334 	uint64_t prev_enable;
335 	uint64_t size;
336 	TRAP_TRACE_CTL	*ctlp;
337 
338 	ctlp = &trap_trace_ctl[cpuid];
339 	if (mach_htraptrace_enable) {
340 		if ((ctlp->d.hvaddr_base != htrap_tr0) || (!htrap_tr0_inuse)) {
341 			ret = hv_ttrace_buf_info(&prev_buf, &prev_bufsize);
342 			if ((ret == H_EOK) && (prev_bufsize != 0)) {
343 				cmn_err(CE_CONT,
344 				    "!cpu%d: previous HV traptrace buffer of "
345 				    "size 0x%lx at address 0x%lx", cpuid,
346 				    prev_bufsize, prev_buf);
347 			}
348 
349 			ret = hv_ttrace_buf_conf(ctlp->d.hpaddr_base,
350 			    HTRAP_TSIZE / (sizeof (struct htrap_trace_record)),
351 			    &size);
352 			if (ret == H_EOK) {
353 				ret = hv_ttrace_enable(\
354 				    (uint64_t)TRAP_TENABLE_ALL, &prev_enable);
355 				if (ret != H_EOK) {
356 					cmn_err(CE_WARN,
357 					    "!cpu%d: HV traptracing not "
358 					    "enabled, ta: 0x%x returned error: "
359 					    "%ld", cpuid, TTRACE_ENABLE, ret);
360 				} else {
361 					if (ctlp->d.hvaddr_base == htrap_tr0)
362 						htrap_tr0_inuse = 1;
363 				}
364 			} else {
365 				cmn_err(CE_WARN,
366 				    "!cpu%d: HV traptrace buffer not "
367 				    "configured, ta: 0x%x returned error: %ld",
368 				    cpuid, TTRACE_BUF_CONF, ret);
369 			}
370 			/*
371 			 * set hvaddr_base to NULL when traptrace buffer
372 			 * registration fails
373 			 */
374 			if (ret != H_EOK) {
375 				ctlp->d.hvaddr_base = NULL;
376 				ctlp->d.hlimit = 0;
377 				ctlp->d.hpaddr_base = NULL;
378 			}
379 		}
380 	} else {
381 		ret = hv_ttrace_buf_info(&prev_buf, &prev_bufsize);
382 		if ((ret == H_EOK) && (prev_bufsize != 0)) {
383 			ret = hv_ttrace_enable((uint64_t)TRAP_TDISABLE_ALL,
384 			    &prev_enable);
385 			if (ret == H_EOK) {
386 				if (ctlp->d.hvaddr_base == htrap_tr0)
387 					htrap_tr0_inuse = 0;
388 				ctlp->d.hvaddr_base = NULL;
389 				ctlp->d.hlimit = 0;
390 				ctlp->d.hpaddr_base = NULL;
391 			} else
392 				cmn_err(CE_WARN,
393 				    "!cpu%d: HV traptracing is not disabled, "
394 				    "ta: 0x%x returned error: %ld",
395 				    cpuid, TTRACE_ENABLE, ret);
396 		}
397 	}
398 }
399 
400 /*
401  * This function allocates hypervisor traptrace buffer
402  */
403 void
404 mach_htraptrace_init(void)
405 {
406 	if (mach_htraptrace_enable && (max_ncpus > 1))
407 		httrace_buf = contig_mem_alloc_align((HTRAP_TSIZE *
408 		    max_ncpus), HTRAP_TSIZE);
409 	else
410 		httrace_buf = NULL;
411 }
412 
413 /*
414  * This function cleans up the hypervisor traptrace buffer
415  */
416 void
417 mach_htraptrace_cleanup(int cpuid)
418 {
419 	int i;
420 	TRAP_TRACE_CTL  *ctlp;
421 	caddr_t newbuf;
422 
423 	if (mach_htraptrace_enable) {
424 		ctlp = &trap_trace_ctl[cpuid];
425 		newbuf = ctlp->d.hvaddr_base;
426 		i = (newbuf - httrace_buf) / HTRAP_TSIZE;
427 		if (((newbuf - httrace_buf) % HTRAP_TSIZE == 0) &&
428 		    ((i >= 0) && (i < max_ncpus))) {
429 			bzero(newbuf, HTRAP_TSIZE);
430 		} else if (newbuf == htrap_tr0) {
431 			bzero(htrap_tr0, (HTRAP_TSIZE));
432 		} else {
433 			cmn_err(CE_WARN, "failed to free trap trace buffer "
434 			    "from cpu%d", cpuid);
435 		}
436 		ctlp->d.hvaddr_base = NULL;
437 		ctlp->d.hlimit = 0;
438 		ctlp->d.hpaddr_base = NULL;
439 	}
440 }
441