xref: /xnu-11417.140.69/osfmk/console/serial_console.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
1*43a90889SApple OSS Distributions /*
2*43a90889SApple OSS Distributions  * Copyright (c) 2000-2020 Apple, Inc. All rights reserved.
3*43a90889SApple OSS Distributions  *
4*43a90889SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*43a90889SApple OSS Distributions  *
6*43a90889SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*43a90889SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*43a90889SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*43a90889SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*43a90889SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*43a90889SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*43a90889SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*43a90889SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*43a90889SApple OSS Distributions  *
15*43a90889SApple OSS Distributions  * Please obtain a copy of the License at
16*43a90889SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*43a90889SApple OSS Distributions  *
18*43a90889SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*43a90889SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*43a90889SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*43a90889SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*43a90889SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*43a90889SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*43a90889SApple OSS Distributions  * limitations under the License.
25*43a90889SApple OSS Distributions  *
26*43a90889SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*43a90889SApple OSS Distributions  */
28*43a90889SApple OSS Distributions 
29*43a90889SApple OSS Distributions #ifdef __x86_64__
30*43a90889SApple OSS Distributions #include <i386/mp.h>
31*43a90889SApple OSS Distributions #include <i386/cpu_data.h>
32*43a90889SApple OSS Distributions #include <i386/bit_routines.h>
33*43a90889SApple OSS Distributions #include <i386/machine_routines.h>
34*43a90889SApple OSS Distributions #include <i386/misc_protos.h>
35*43a90889SApple OSS Distributions #include <i386/serial_io.h>
36*43a90889SApple OSS Distributions #endif /* __x86_64__ */
37*43a90889SApple OSS Distributions 
38*43a90889SApple OSS Distributions #include <machine/machine_cpu.h>
39*43a90889SApple OSS Distributions #include <libkern/OSAtomic.h>
40*43a90889SApple OSS Distributions #include <vm/vm_kern_xnu.h>
41*43a90889SApple OSS Distributions #include <vm/vm_map.h>
42*43a90889SApple OSS Distributions #include <console/video_console.h>
43*43a90889SApple OSS Distributions #include <console/serial_protos.h>
44*43a90889SApple OSS Distributions #include <kern/startup.h>
45*43a90889SApple OSS Distributions #include <kern/thread.h>
46*43a90889SApple OSS Distributions #include <kern/cpu_data.h>
47*43a90889SApple OSS Distributions #include <kern/sched_prim.h>
48*43a90889SApple OSS Distributions #include <libkern/section_keywords.h>
49*43a90889SApple OSS Distributions 
50*43a90889SApple OSS Distributions #if __arm64__
51*43a90889SApple OSS Distributions #include <machine/machine_routines.h>
52*43a90889SApple OSS Distributions #include <arm/cpu_data_internal.h>
53*43a90889SApple OSS Distributions #endif
54*43a90889SApple OSS Distributions 
55*43a90889SApple OSS Distributions #ifdef CONFIG_XNUPOST
56*43a90889SApple OSS Distributions #include <tests/xnupost.h>
57*43a90889SApple OSS Distributions kern_return_t console_serial_test(void);
58*43a90889SApple OSS Distributions kern_return_t console_serial_parallel_log_tests(void);
59*43a90889SApple OSS Distributions #endif
60*43a90889SApple OSS Distributions 
61*43a90889SApple OSS Distributions /* Structure representing the console ring buffer. */
62*43a90889SApple OSS Distributions static struct {
63*43a90889SApple OSS Distributions 	/* The ring buffer backing store. */
64*43a90889SApple OSS Distributions 	char *buffer;
65*43a90889SApple OSS Distributions 
66*43a90889SApple OSS Distributions 	/* The total length of the ring buffer. */
67*43a90889SApple OSS Distributions 	int len;
68*43a90889SApple OSS Distributions 
69*43a90889SApple OSS Distributions 	/**
70*43a90889SApple OSS Distributions 	 * The number of characters that have been written into the buffer that need
71*43a90889SApple OSS Distributions 	 * to be drained.
72*43a90889SApple OSS Distributions 	 */
73*43a90889SApple OSS Distributions 	int used;
74*43a90889SApple OSS Distributions 
75*43a90889SApple OSS Distributions 	/**
76*43a90889SApple OSS Distributions 	 * Number of reserved regions in the buffer. These are regions that are
77*43a90889SApple OSS Distributions 	 * currently being written into by various CPUs. We use this as a way of
78*43a90889SApple OSS Distributions 	 * determining when it's safe to drain the buffer.
79*43a90889SApple OSS Distributions 	 */
80*43a90889SApple OSS Distributions 	int nreserved;
81*43a90889SApple OSS Distributions 
82*43a90889SApple OSS Distributions 	/* The location in the buffer thats written to next. */
83*43a90889SApple OSS Distributions 	char *write_ptr;
84*43a90889SApple OSS Distributions 
85*43a90889SApple OSS Distributions 	/* The location in the buffer that will be drained next. */
86*43a90889SApple OSS Distributions 	char *read_ptr;
87*43a90889SApple OSS Distributions 
88*43a90889SApple OSS Distributions 	/* Synchronizes the flushing of the ring buffer to hardware */
89*43a90889SApple OSS Distributions 	lck_mtx_t flush_lock;
90*43a90889SApple OSS Distributions 
91*43a90889SApple OSS Distributions 	/**
92*43a90889SApple OSS Distributions 	 * Synchronizes reserving space in the ring buffer and ensures that only
93*43a90889SApple OSS Distributions 	 * completed writes are flushed.
94*43a90889SApple OSS Distributions 	 */
95*43a90889SApple OSS Distributions 	lck_ticket_t write_lock;
96*43a90889SApple OSS Distributions } console_ring;
97*43a90889SApple OSS Distributions 
98*43a90889SApple OSS Distributions /**
99*43a90889SApple OSS Distributions  * We don't dedicate any buffer space to specific CPUs, but this value is used
100*43a90889SApple OSS Distributions  * to scale the size of the console buffer by the number of CPUs.
101*43a90889SApple OSS Distributions  *
102*43a90889SApple OSS Distributions  * How many bytes-per-cpu to allocate in the console ring buffer. Also affects
103*43a90889SApple OSS Distributions  * the maximum number of bytes a single console thread can drain.
104*43a90889SApple OSS Distributions  */
105*43a90889SApple OSS Distributions #define CPU_CONS_BUF_SIZE 256
106*43a90889SApple OSS Distributions 
107*43a90889SApple OSS Distributions /* Scale the size of the console ring buffer by the number of CPUs. */
108*43a90889SApple OSS Distributions #define KERN_CONSOLE_RING_SIZE vm_map_round_page(CPU_CONS_BUF_SIZE * (MAX_CPUS + 1), PAGE_SIZE - 1)
109*43a90889SApple OSS Distributions 
110*43a90889SApple OSS Distributions #define MAX_FLUSH_SIZE_LOCK_HELD 16
111*43a90889SApple OSS Distributions #define MAX_TOTAL_FLUSH_SIZE (MAX(2, MAX_CPUS) * CPU_CONS_BUF_SIZE)
112*43a90889SApple OSS Distributions 
113*43a90889SApple OSS Distributions extern int serial_getc(void);
114*43a90889SApple OSS Distributions extern void serial_putc_options(char, bool);
115*43a90889SApple OSS Distributions 
116*43a90889SApple OSS Distributions #if DEBUG || DEVELOPMENT
117*43a90889SApple OSS Distributions TUNABLE(bool, allow_printf_from_interrupts_disabled_context, "nointr_consio", false);
118*43a90889SApple OSS Distributions #else
119*43a90889SApple OSS Distributions #define allow_printf_from_interrupts_disabled_context false
120*43a90889SApple OSS Distributions #endif
121*43a90889SApple OSS Distributions 
122*43a90889SApple OSS Distributions SECURITY_READ_ONLY_EARLY(struct console_ops) cons_ops[] = {
123*43a90889SApple OSS Distributions 	{
124*43a90889SApple OSS Distributions 		.putc = serial_putc_options, .getc = _serial_getc,
125*43a90889SApple OSS Distributions 	},
126*43a90889SApple OSS Distributions 	{
127*43a90889SApple OSS Distributions 		.putc = vcputc_options, .getc = _vcgetc,
128*43a90889SApple OSS Distributions 	},
129*43a90889SApple OSS Distributions };
130*43a90889SApple OSS Distributions 
131*43a90889SApple OSS Distributions SECURITY_READ_ONLY_EARLY(uint32_t) nconsops = (sizeof cons_ops / sizeof cons_ops[0]);
132*43a90889SApple OSS Distributions 
133*43a90889SApple OSS Distributions #if __x86_64__
134*43a90889SApple OSS Distributions uint32_t cons_ops_index = VC_CONS_OPS;
135*43a90889SApple OSS Distributions #else
136*43a90889SApple OSS Distributions SECURITY_READ_ONLY_LATE(uint32_t) cons_ops_index = VC_CONS_OPS;
137*43a90889SApple OSS Distributions #endif
138*43a90889SApple OSS Distributions 
139*43a90889SApple OSS Distributions LCK_GRP_DECLARE(console_lck_grp, "console");
140*43a90889SApple OSS Distributions 
141*43a90889SApple OSS Distributions /* If the NMI string is entered into the console, the system will enter the debugger. */
142*43a90889SApple OSS Distributions #define NMI_STRING_SIZE 32
143*43a90889SApple OSS Distributions char nmi_string[NMI_STRING_SIZE] = "afDIGHr84A84jh19Kphgp428DNPdnapq";
144*43a90889SApple OSS Distributions static int nmi_counter           = 0;
145*43a90889SApple OSS Distributions 
146*43a90889SApple OSS Distributions /**
147*43a90889SApple OSS Distributions  * This is used to prevent console output from going through the console ring
148*43a90889SApple OSS Distributions  * buffer synchronization in cases where that could cause issues (e.g., during
149*43a90889SApple OSS Distributions  * panics/stackshots and going down for sleep).
150*43a90889SApple OSS Distributions  */
151*43a90889SApple OSS Distributions static bool console_suspended = false;
152*43a90889SApple OSS Distributions 
153*43a90889SApple OSS Distributions /**
154*43a90889SApple OSS Distributions  * Controls console output for underlying serial or video console.
155*43a90889SApple OSS Distributions  * To be used only by core console and init accessors.
156*43a90889SApple OSS Distributions  */
157*43a90889SApple OSS Distributions int disableConsoleOutput;
158*43a90889SApple OSS Distributions 
159*43a90889SApple OSS Distributions /**
160*43a90889SApple OSS Distributions  * Enforce policies around when console I/O is allowed. Most importantly about
161*43a90889SApple OSS Distributions  * not performing console I/O while interrupts are disabled (which can cause
162*43a90889SApple OSS Distributions  * serious latency issues).
163*43a90889SApple OSS Distributions  *
164*43a90889SApple OSS Distributions  * @return True if console I/O should be allowed, false otherwise.
165*43a90889SApple OSS Distributions  */
166*43a90889SApple OSS Distributions static inline bool
console_io_allowed(void)167*43a90889SApple OSS Distributions console_io_allowed(void)
168*43a90889SApple OSS Distributions {
169*43a90889SApple OSS Distributions 	if (!allow_printf_from_interrupts_disabled_context &&
170*43a90889SApple OSS Distributions 	    !console_suspended &&
171*43a90889SApple OSS Distributions 	    startup_phase >= STARTUP_SUB_EARLY_BOOT &&
172*43a90889SApple OSS Distributions 	    !ml_get_interrupts_enabled()) {
173*43a90889SApple OSS Distributions #if defined(__arm64__) || DEBUG || DEVELOPMENT
174*43a90889SApple OSS Distributions 		panic("Console I/O from interrupt-disabled context");
175*43a90889SApple OSS Distributions #else
176*43a90889SApple OSS Distributions 		return false;
177*43a90889SApple OSS Distributions #endif
178*43a90889SApple OSS Distributions 	}
179*43a90889SApple OSS Distributions 
180*43a90889SApple OSS Distributions 	return true;
181*43a90889SApple OSS Distributions }
182*43a90889SApple OSS Distributions 
183*43a90889SApple OSS Distributions /**
184*43a90889SApple OSS Distributions  * Initialize the console ring buffer and console lock. It's still possible to
185*43a90889SApple OSS Distributions  * call console_write() before initializing the ring buffer. In that case the
186*43a90889SApple OSS Distributions  * data will get outputted directly to the underlying serial/video console
187*43a90889SApple OSS Distributions  * without synchronization.
188*43a90889SApple OSS Distributions  *
189*43a90889SApple OSS Distributions  * This function is also safe to call multiple times. Any call after the first
190*43a90889SApple OSS Distributions  * will return early without doing anything.
191*43a90889SApple OSS Distributions  */
192*43a90889SApple OSS Distributions void
console_init(void)193*43a90889SApple OSS Distributions console_init(void)
194*43a90889SApple OSS Distributions {
195*43a90889SApple OSS Distributions 	if (console_ring.len != 0) {
196*43a90889SApple OSS Distributions 		return;
197*43a90889SApple OSS Distributions 	}
198*43a90889SApple OSS Distributions 
199*43a90889SApple OSS Distributions 	kmem_alloc(kernel_map, (vm_offset_t *)&console_ring.buffer,
200*43a90889SApple OSS Distributions 	    KERN_CONSOLE_RING_SIZE + ptoa(2), KMA_NOFAIL | KMA_PERMANENT |
201*43a90889SApple OSS Distributions 	    KMA_KOBJECT | KMA_PERMANENT | KMA_GUARD_FIRST | KMA_GUARD_LAST |
202*43a90889SApple OSS Distributions 	    KMA_ZERO | KMA_DATA, VM_KERN_MEMORY_OSFMK);
203*43a90889SApple OSS Distributions 
204*43a90889SApple OSS Distributions 	console_ring.buffer   += PAGE_SIZE; /* Skip past the first guard page. */
205*43a90889SApple OSS Distributions 	console_ring.len       = KERN_CONSOLE_RING_SIZE;
206*43a90889SApple OSS Distributions 	console_ring.used      = 0;
207*43a90889SApple OSS Distributions 	console_ring.nreserved = 0;
208*43a90889SApple OSS Distributions 	console_ring.read_ptr  = console_ring.buffer;
209*43a90889SApple OSS Distributions 	console_ring.write_ptr = console_ring.buffer;
210*43a90889SApple OSS Distributions 
211*43a90889SApple OSS Distributions 	lck_mtx_init(&console_ring.flush_lock, &console_lck_grp, LCK_ATTR_NULL);
212*43a90889SApple OSS Distributions 	lck_ticket_init(&console_ring.write_lock, &console_lck_grp);
213*43a90889SApple OSS Distributions }
214*43a90889SApple OSS Distributions 
215*43a90889SApple OSS Distributions /**
216*43a90889SApple OSS Distributions  * Returns true when the console has already been initialized.
217*43a90889SApple OSS Distributions  */
218*43a90889SApple OSS Distributions static inline bool
is_console_initialized(void)219*43a90889SApple OSS Distributions is_console_initialized(void)
220*43a90889SApple OSS Distributions {
221*43a90889SApple OSS Distributions 	return console_ring.len == KERN_CONSOLE_RING_SIZE;
222*43a90889SApple OSS Distributions }
223*43a90889SApple OSS Distributions 
224*43a90889SApple OSS Distributions /**
225*43a90889SApple OSS Distributions  * Return the index to the currently selected console (serial/video). This is
226*43a90889SApple OSS Distributions  * an index into the "cons_ops[]" array of function pointer structs.
227*43a90889SApple OSS Distributions  */
228*43a90889SApple OSS Distributions static inline uint32_t
get_cons_ops_index(void)229*43a90889SApple OSS Distributions get_cons_ops_index(void)
230*43a90889SApple OSS Distributions {
231*43a90889SApple OSS Distributions 	uint32_t idx = cons_ops_index;
232*43a90889SApple OSS Distributions 
233*43a90889SApple OSS Distributions 	if (idx >= nconsops) {
234*43a90889SApple OSS Distributions 		panic("Bad cons_ops_index: %d", idx);
235*43a90889SApple OSS Distributions 	}
236*43a90889SApple OSS Distributions 
237*43a90889SApple OSS Distributions 	return idx;
238*43a90889SApple OSS Distributions }
239*43a90889SApple OSS Distributions 
240*43a90889SApple OSS Distributions /**
241*43a90889SApple OSS Distributions  * Helper function for outputting a character to the underlying console
242*43a90889SApple OSS Distributions  * (either video or serial) with the possibility of sleeping waiting for
243*43a90889SApple OSS Distributions  * an interrupt indicating the console is ready.
244*43a90889SApple OSS Distributions  *
245*43a90889SApple OSS Distributions  * @note assumes console_ring.read lock is held if poll == false
246*43a90889SApple OSS Distributions  *
247*43a90889SApple OSS Distributions  * @param c The character to print.
248*43a90889SApple OSS Distributions  * @param poll Whether or not this call should poll instead of going to sleep
249*43a90889SApple OSS Distributions  *             waiting for an interrupt when the hardware device isn't ready
250*43a90889SApple OSS Distributions  */
251*43a90889SApple OSS Distributions static inline void
_cnputc(char c,bool poll)252*43a90889SApple OSS Distributions _cnputc(char c, bool poll)
253*43a90889SApple OSS Distributions {
254*43a90889SApple OSS Distributions 	bool in_debugger = (kernel_debugger_entry_count > 0);
255*43a90889SApple OSS Distributions 	const uint32_t idx = get_cons_ops_index();
256*43a90889SApple OSS Distributions 
257*43a90889SApple OSS Distributions 	poll = poll || in_debugger;
258*43a90889SApple OSS Distributions 
259*43a90889SApple OSS Distributions 	if (c == '\n') {
260*43a90889SApple OSS Distributions 		_cnputc('\r', poll);
261*43a90889SApple OSS Distributions 	}
262*43a90889SApple OSS Distributions 
263*43a90889SApple OSS Distributions 	cons_ops[idx].putc(c, poll);
264*43a90889SApple OSS Distributions }
265*43a90889SApple OSS Distributions 
266*43a90889SApple OSS Distributions /**
267*43a90889SApple OSS Distributions  * Helper function for outputting characters directly to the underlying console
268*43a90889SApple OSS Distributions  * (either video or serial).
269*43a90889SApple OSS Distributions  *
270*43a90889SApple OSS Distributions  * @note disableConsoleOutput is to be used only by core console and init accessors
271*43a90889SApple OSS Distributions  *       such as this function. Returns early if the serial output is disabled.
272*43a90889SApple OSS Distributions  *
273*43a90889SApple OSS Distributions  * @param c The array of characters to print.
274*43a90889SApple OSS Distributions  * @param poll Whether or not this call should poll instead of going to sleep
275*43a90889SApple OSS Distributions  *             waiting for an interrupt when the hardware device isn't ready
276*43a90889SApple OSS Distributions  * @param size The number of characters to print to the console.
277*43a90889SApple OSS Distributions  */
278*43a90889SApple OSS Distributions static inline void
_cnputs(char * c,int size,bool poll)279*43a90889SApple OSS Distributions _cnputs(char *c, int size, bool poll)
280*43a90889SApple OSS Distributions {
281*43a90889SApple OSS Distributions 	if (disableConsoleOutput) {
282*43a90889SApple OSS Distributions 		return;
283*43a90889SApple OSS Distributions 	}
284*43a90889SApple OSS Distributions 
285*43a90889SApple OSS Distributions 	assert(c != NULL);
286*43a90889SApple OSS Distributions 
287*43a90889SApple OSS Distributions 	while (size-- > 0) {
288*43a90889SApple OSS Distributions 		_cnputc(*c, poll);
289*43a90889SApple OSS Distributions 		c++;
290*43a90889SApple OSS Distributions 	}
291*43a90889SApple OSS Distributions }
292*43a90889SApple OSS Distributions 
293*43a90889SApple OSS Distributions /**
294*43a90889SApple OSS Distributions  * Attempt to reserve space for a number of characters in the console ring
295*43a90889SApple OSS Distributions  * buffer. Space in the ring buffer must be reserved before new characters can
296*43a90889SApple OSS Distributions  * be entered.
297*43a90889SApple OSS Distributions  *
298*43a90889SApple OSS Distributions  * Every call to this function should be paired with a corresponding call to
299*43a90889SApple OSS Distributions  * console_ring_unreserve_space().
300*43a90889SApple OSS Distributions  *
301*43a90889SApple OSS Distributions  * @note If space is successfully reserved, this will disable preemption because
302*43a90889SApple OSS Distributions  *       otherwise, console_ring_try_empty() could take arbitrarily long.
303*43a90889SApple OSS Distributions  *
304*43a90889SApple OSS Distributions  * @param nchars The number of characters to reserve.
305*43a90889SApple OSS Distributions  *
306*43a90889SApple OSS Distributions  * @return If the wanted number of characters could not be reserved, then return
307*43a90889SApple OSS Distributions  *         NULL. Otherwise, return a pointer to the beginning of the reserved
308*43a90889SApple OSS Distributions  *         space.
309*43a90889SApple OSS Distributions  */
310*43a90889SApple OSS Distributions static inline char*
console_ring_reserve_space(int nchars)311*43a90889SApple OSS Distributions console_ring_reserve_space(int nchars)
312*43a90889SApple OSS Distributions {
313*43a90889SApple OSS Distributions 	char *write_ptr = NULL;
314*43a90889SApple OSS Distributions 	lck_ticket_lock(&console_ring.write_lock, &console_lck_grp);
315*43a90889SApple OSS Distributions 	if ((console_ring.len - console_ring.used) >= nchars) {
316*43a90889SApple OSS Distributions 		console_ring.used += nchars;
317*43a90889SApple OSS Distributions 		mp_disable_preemption();
318*43a90889SApple OSS Distributions 		os_atomic_inc(&console_ring.nreserved, relaxed);
319*43a90889SApple OSS Distributions 
320*43a90889SApple OSS Distributions 		/* Return out the pointer to the beginning of the just reserved space. */
321*43a90889SApple OSS Distributions 		write_ptr = console_ring.write_ptr;
322*43a90889SApple OSS Distributions 
323*43a90889SApple OSS Distributions 		/* Move the console ring's write pointer to the beginning of the next free space. */
324*43a90889SApple OSS Distributions 		const ptrdiff_t write_index = console_ring.write_ptr - console_ring.buffer;
325*43a90889SApple OSS Distributions 		console_ring.write_ptr = console_ring.buffer + ((write_index + nchars) % console_ring.len);
326*43a90889SApple OSS Distributions 	}
327*43a90889SApple OSS Distributions 	lck_ticket_unlock(&console_ring.write_lock);
328*43a90889SApple OSS Distributions 	return write_ptr;
329*43a90889SApple OSS Distributions }
330*43a90889SApple OSS Distributions 
331*43a90889SApple OSS Distributions /**
332*43a90889SApple OSS Distributions  * Decrement the number of reserved spaces in the console ring (now that the data
333*43a90889SApple OSS Distributions  * has been written) and re-enable preemption.
334*43a90889SApple OSS Distributions  *
335*43a90889SApple OSS Distributions  * Every call to this function should be paired with a corresponding call to
336*43a90889SApple OSS Distributions  * console_ring_reserve_space().
337*43a90889SApple OSS Distributions  */
338*43a90889SApple OSS Distributions static inline void
console_ring_unreserve_space(void)339*43a90889SApple OSS Distributions console_ring_unreserve_space(void)
340*43a90889SApple OSS Distributions {
341*43a90889SApple OSS Distributions 	assert(console_ring.nreserved > 0);
342*43a90889SApple OSS Distributions 
343*43a90889SApple OSS Distributions 	os_atomic_dec(&console_ring.nreserved, relaxed);
344*43a90889SApple OSS Distributions 	mp_enable_preemption();
345*43a90889SApple OSS Distributions }
346*43a90889SApple OSS Distributions 
347*43a90889SApple OSS Distributions /**
348*43a90889SApple OSS Distributions  * Write a single character into the console ring buffer and handle moving the
349*43a90889SApple OSS Distributions  * write pointer circularly around the buffer.
350*43a90889SApple OSS Distributions  *
351*43a90889SApple OSS Distributions  * @note Space to write this character must have already been reserved using
352*43a90889SApple OSS Distributions  *       console_ring_reserve_space().
353*43a90889SApple OSS Distributions  *
354*43a90889SApple OSS Distributions  * @param write_ptr Pointer into the reserved space in the buffer to write the
355*43a90889SApple OSS Distributions  *                  character. This pointer will get moved to the next valid
356*43a90889SApple OSS Distributions  *                  location to write a character so the same pointer can be
357*43a90889SApple OSS Distributions  *                  passed into subsequent calls to write multiple characters.
358*43a90889SApple OSS Distributions  * @param ch The character to insert into the ring buffer.
359*43a90889SApple OSS Distributions  */
360*43a90889SApple OSS Distributions static inline void
console_ring_put(char ** write_ptr,char ch)361*43a90889SApple OSS Distributions console_ring_put(char **write_ptr, char ch)
362*43a90889SApple OSS Distributions {
363*43a90889SApple OSS Distributions 	assert(console_ring.nreserved > 0);
364*43a90889SApple OSS Distributions 	**write_ptr = ch;
365*43a90889SApple OSS Distributions 	++(*write_ptr);
366*43a90889SApple OSS Distributions 	if ((*write_ptr - console_ring.buffer) == console_ring.len) {
367*43a90889SApple OSS Distributions 		*write_ptr = console_ring.buffer;
368*43a90889SApple OSS Distributions 	}
369*43a90889SApple OSS Distributions }
370*43a90889SApple OSS Distributions 
371*43a90889SApple OSS Distributions /**
372*43a90889SApple OSS Distributions  * Attempt to drain the console ring buffer if no other CPUs are already doing
373*43a90889SApple OSS Distributions  * so.
374*43a90889SApple OSS Distributions  *
375*43a90889SApple OSS Distributions  * @param fail_fast If true, this function returns immediately instead of
376*43a90889SApple OSS Distributions  *                  sleeping if the thread fails to acquire the console flush
377*43a90889SApple OSS Distributions  *                  mutex.
378*43a90889SApple OSS Distributions  *
379*43a90889SApple OSS Distributions  * @note This function should not be called with preemption disabled.
380*43a90889SApple OSS Distributions  *
381*43a90889SApple OSS Distributions  * @note To prevent one CPU from holding the console lock for too long, only
382*43a90889SApple OSS Distributions  *       MAX_FLUSH_SIZE_LOCK_HELD number of characters can be drained at a time
383*43a90889SApple OSS Distributions  *       with the lock held. The lock will be dropped between each drain of size
384*43a90889SApple OSS Distributions  *       MAX_FLUSH_SIZE_LOCK_HELD to allow another CPU to grab the lock. If
385*43a90889SApple OSS Distributions  *       another CPU grabs the lock, then the original thread can stop draining
386*43a90889SApple OSS Distributions  *       and return instead of sleeping for the lock.
387*43a90889SApple OSS Distributions  *
388*43a90889SApple OSS Distributions  * @note To prevent one thread from being the drain thread for too long (presumably
389*43a90889SApple OSS Distributions  *       that thread has other things it wants to do besides draining serial), the
390*43a90889SApple OSS Distributions  *       total number of characters a single call to this function can drain is
391*43a90889SApple OSS Distributions  *       restricted to MAX_TOTAL_FLUSH_SIZE.
392*43a90889SApple OSS Distributions  */
393*43a90889SApple OSS Distributions static void
console_ring_try_empty(bool fail_fast)394*43a90889SApple OSS Distributions console_ring_try_empty(bool fail_fast)
395*43a90889SApple OSS Distributions {
396*43a90889SApple OSS Distributions 	char flush_buf[MAX_FLUSH_SIZE_LOCK_HELD];
397*43a90889SApple OSS Distributions 
398*43a90889SApple OSS Distributions 	int nchars_out       = 0;
399*43a90889SApple OSS Distributions 	int total_chars_out  = 0;
400*43a90889SApple OSS Distributions 	int size_before_wrap = 0;
401*43a90889SApple OSS Distributions 	bool in_debugger = (kernel_debugger_entry_count > 0);
402*43a90889SApple OSS Distributions 
403*43a90889SApple OSS Distributions 	if (__improbable(!console_io_allowed()) || (!in_debugger && get_preemption_level() != 0)) {
404*43a90889SApple OSS Distributions 		return;
405*43a90889SApple OSS Distributions 	}
406*43a90889SApple OSS Distributions 
407*43a90889SApple OSS Distributions 	do {
408*43a90889SApple OSS Distributions 		if (__probable(!in_debugger) && fail_fast && !lck_mtx_try_lock(&console_ring.flush_lock)) {
409*43a90889SApple OSS Distributions 			return;
410*43a90889SApple OSS Distributions 		} else if (__probable(!in_debugger) && !fail_fast) {
411*43a90889SApple OSS Distributions 			lck_mtx_lock(&console_ring.flush_lock);
412*43a90889SApple OSS Distributions 		}
413*43a90889SApple OSS Distributions 
414*43a90889SApple OSS Distributions 		if (__probable(!in_debugger)) {
415*43a90889SApple OSS Distributions 			lck_ticket_lock(&console_ring.write_lock, &console_lck_grp);
416*43a90889SApple OSS Distributions 
417*43a90889SApple OSS Distributions 			/**
418*43a90889SApple OSS Distributions 			 * If we've managed to grab the write lock, but there's still space
419*43a90889SApple OSS Distributions 			 * reserved in the buffer, then other CPUs are actively writing into
420*43a90889SApple OSS Distributions 			 * the ring, wait for them to finish.
421*43a90889SApple OSS Distributions 			 */
422*43a90889SApple OSS Distributions 			while (os_atomic_load(&console_ring.nreserved, relaxed) > 0) {
423*43a90889SApple OSS Distributions 				cpu_pause();
424*43a90889SApple OSS Distributions 			}
425*43a90889SApple OSS Distributions 		}
426*43a90889SApple OSS Distributions 
427*43a90889SApple OSS Distributions 		/* Try small chunk at a time, so we allow writes from other cpus into the buffer. */
428*43a90889SApple OSS Distributions 		nchars_out = MIN(console_ring.used, (int)sizeof(flush_buf));
429*43a90889SApple OSS Distributions 
430*43a90889SApple OSS Distributions 		/* Account for data to be read before wrap around. */
431*43a90889SApple OSS Distributions 		size_before_wrap = (int)((console_ring.buffer + console_ring.len) - console_ring.read_ptr);
432*43a90889SApple OSS Distributions 		if (nchars_out > size_before_wrap) {
433*43a90889SApple OSS Distributions 			nchars_out = size_before_wrap;
434*43a90889SApple OSS Distributions 		}
435*43a90889SApple OSS Distributions 
436*43a90889SApple OSS Distributions 		/**
437*43a90889SApple OSS Distributions 		 * Copy the characters to be drained into a separate flush buffer, and
438*43a90889SApple OSS Distributions 		 * move the console read pointer to the next chunk of data that needs to
439*43a90889SApple OSS Distributions 		 * be drained.
440*43a90889SApple OSS Distributions 		 */
441*43a90889SApple OSS Distributions 		if (nchars_out > 0) {
442*43a90889SApple OSS Distributions 			memcpy(flush_buf, console_ring.read_ptr, nchars_out);
443*43a90889SApple OSS Distributions 			const ptrdiff_t read_index = console_ring.read_ptr - console_ring.buffer;
444*43a90889SApple OSS Distributions 			console_ring.read_ptr = console_ring.buffer + ((read_index + nchars_out) % console_ring.len);
445*43a90889SApple OSS Distributions 			console_ring.used -= nchars_out;
446*43a90889SApple OSS Distributions 		}
447*43a90889SApple OSS Distributions 
448*43a90889SApple OSS Distributions 		if (__probable(!in_debugger)) {
449*43a90889SApple OSS Distributions 			lck_ticket_unlock(&console_ring.write_lock);
450*43a90889SApple OSS Distributions 		}
451*43a90889SApple OSS Distributions 
452*43a90889SApple OSS Distributions 		/**
453*43a90889SApple OSS Distributions 		 * Output characters to the underlying console (serial/video). We should
454*43a90889SApple OSS Distributions 		 * only poll if the console is suspended.
455*43a90889SApple OSS Distributions 		 */
456*43a90889SApple OSS Distributions 		if (nchars_out > 0) {
457*43a90889SApple OSS Distributions 			total_chars_out += nchars_out;
458*43a90889SApple OSS Distributions 			_cnputs(flush_buf, nchars_out, console_suspended);
459*43a90889SApple OSS Distributions 		}
460*43a90889SApple OSS Distributions 
461*43a90889SApple OSS Distributions 		if (__probable(!in_debugger)) {
462*43a90889SApple OSS Distributions 			lck_mtx_unlock(&console_ring.flush_lock);
463*43a90889SApple OSS Distributions 		}
464*43a90889SApple OSS Distributions 
465*43a90889SApple OSS Distributions 		/**
466*43a90889SApple OSS Distributions 		 * Prevent this thread from sleeping on the lock again if another thread
467*43a90889SApple OSS Distributions 		 * grabs it after we drop it.
468*43a90889SApple OSS Distributions 		 */
469*43a90889SApple OSS Distributions 		fail_fast = true;
470*43a90889SApple OSS Distributions 
471*43a90889SApple OSS Distributions 		/*
472*43a90889SApple OSS Distributions 		 * In case we end up being the console drain thread for far too long,
473*43a90889SApple OSS Distributions 		 * break out. Except in panic/suspend cases where we should clear out
474*43a90889SApple OSS Distributions 		 * the full buffer.
475*43a90889SApple OSS Distributions 		 */
476*43a90889SApple OSS Distributions 		if (!console_suspended && (total_chars_out >= MAX_TOTAL_FLUSH_SIZE)) {
477*43a90889SApple OSS Distributions 			break;
478*43a90889SApple OSS Distributions 		}
479*43a90889SApple OSS Distributions 	} while (nchars_out > 0);
480*43a90889SApple OSS Distributions }
481*43a90889SApple OSS Distributions 
482*43a90889SApple OSS Distributions /**
483*43a90889SApple OSS Distributions  * Notify the console subystem that all following console writes should skip
484*43a90889SApple OSS Distributions  * synchronization and get outputted directly to the underlying console. This is
485*43a90889SApple OSS Distributions  * important for cases like panic/stackshots and going down for sleep where
486*43a90889SApple OSS Distributions  * assumptions about the state of the system could cause hangs or nested panics.
487*43a90889SApple OSS Distributions  */
488*43a90889SApple OSS Distributions void
console_suspend()489*43a90889SApple OSS Distributions console_suspend()
490*43a90889SApple OSS Distributions {
491*43a90889SApple OSS Distributions 	console_suspended = true;
492*43a90889SApple OSS Distributions 	console_ring_try_empty(false);
493*43a90889SApple OSS Distributions }
494*43a90889SApple OSS Distributions 
495*43a90889SApple OSS Distributions /**
496*43a90889SApple OSS Distributions  * Notify the console subsystem that it is now safe to use the console ring
497*43a90889SApple OSS Distributions  * buffer synchronization when writing console data.
498*43a90889SApple OSS Distributions  */
499*43a90889SApple OSS Distributions void
console_resume()500*43a90889SApple OSS Distributions console_resume()
501*43a90889SApple OSS Distributions {
502*43a90889SApple OSS Distributions 	console_suspended = false;
503*43a90889SApple OSS Distributions }
504*43a90889SApple OSS Distributions 
505*43a90889SApple OSS Distributions /**
506*43a90889SApple OSS Distributions  * Write a string of characters to the underlying video or serial console in a
507*43a90889SApple OSS Distributions  * synchronized manner. By synchronizing access to a global console buffer, this
508*43a90889SApple OSS Distributions  * prevents the serial output from appearing interleaved to the end user when
509*43a90889SApple OSS Distributions  * multiple CPUs are outputting to the console at the same time.
510*43a90889SApple OSS Distributions  *
511*43a90889SApple OSS Distributions  * @note It's safe to call this function even before the console buffer has been
512*43a90889SApple OSS Distributions  *       initialized. In that case, the data will be sent directly to the
513*43a90889SApple OSS Distributions  *       underlying console with no buffering. This is the same for when the
514*43a90889SApple OSS Distributions  *       console is suspended.
515*43a90889SApple OSS Distributions  *
516*43a90889SApple OSS Distributions  * @note disableConsoleOutput is to be used only by core console and init accessors
517*43a90889SApple OSS Distributions  *       such as this function. Returns early if the serial output is disabled and
518*43a90889SApple OSS Distributions  *       skips lock acquisition.
519*43a90889SApple OSS Distributions  *
520*43a90889SApple OSS Distributions  * @param str The string of characters to print.
521*43a90889SApple OSS Distributions  * @param size The number of characters in `str` to print.
522*43a90889SApple OSS Distributions  */
523*43a90889SApple OSS Distributions void
console_write(char * str,int size)524*43a90889SApple OSS Distributions console_write(char *str, int size)
525*43a90889SApple OSS Distributions {
526*43a90889SApple OSS Distributions 	if (disableConsoleOutput) {
527*43a90889SApple OSS Distributions 		return;
528*43a90889SApple OSS Distributions 	}
529*43a90889SApple OSS Distributions 
530*43a90889SApple OSS Distributions 	assert(str != NULL);
531*43a90889SApple OSS Distributions 
532*43a90889SApple OSS Distributions 	char *write_ptr = NULL;
533*43a90889SApple OSS Distributions 	int chunk_size = CPU_CONS_BUF_SIZE;
534*43a90889SApple OSS Distributions 	int i = 0;
535*43a90889SApple OSS Distributions 
536*43a90889SApple OSS Distributions 	if (__improbable(console_suspended || !is_console_initialized() || pmap_in_ppl())) {
537*43a90889SApple OSS Distributions 		/*
538*43a90889SApple OSS Distributions 		 * Output directly to console in the following cases:
539*43a90889SApple OSS Distributions 		 * 1. If this is early in boot before the console has been initialized.
540*43a90889SApple OSS Distributions 		 * 2. If we're heading into suspend.
541*43a90889SApple OSS Distributions 		 * 3. If we're in the kernel debugger for a panic/stackshot. If any of
542*43a90889SApple OSS Distributions 		 *    the other cores happened to halt while holding any of the console
543*43a90889SApple OSS Distributions 		 *    locks, attempting to use the normal path will result in sadness.
544*43a90889SApple OSS Distributions 		 * 4. If we're in the PPL. As we synchronize the ring buffer with a
545*43a90889SApple OSS Distributions 		 *    mutex and preemption is disabled in the PPL, any writes must go
546*43a90889SApple OSS Distributions 		 *    directly to the hardware device.
547*43a90889SApple OSS Distributions 		 */
548*43a90889SApple OSS Distributions 		_cnputs(str, size, true);
549*43a90889SApple OSS Distributions 		return;
550*43a90889SApple OSS Distributions 	} else if (__improbable(!console_io_allowed())) {
551*43a90889SApple OSS Distributions 		return;
552*43a90889SApple OSS Distributions 	}
553*43a90889SApple OSS Distributions 
554*43a90889SApple OSS Distributions 	while (size > 0) {
555*43a90889SApple OSS Distributions 		/**
556*43a90889SApple OSS Distributions 		 * Restrict the maximum number of characters that can be reserved at
557*43a90889SApple OSS Distributions 		 * once. This helps prevent one CPU from reserving too much and starving
558*43a90889SApple OSS Distributions 		 * out the other CPUs.
559*43a90889SApple OSS Distributions 		 */
560*43a90889SApple OSS Distributions 		if (size < chunk_size) {
561*43a90889SApple OSS Distributions 			chunk_size = size;
562*43a90889SApple OSS Distributions 		}
563*43a90889SApple OSS Distributions 
564*43a90889SApple OSS Distributions 		/**
565*43a90889SApple OSS Distributions 		 * Attempt to reserve space in the ring buffer and if that fails, then
566*43a90889SApple OSS Distributions 		 * keep attempting to drain the ring buffer until there's enough space.
567*43a90889SApple OSS Distributions 		 * We can't flush the serial console with preemption disabled so return
568*43a90889SApple OSS Distributions 		 * early to drop the message in that case.
569*43a90889SApple OSS Distributions 		 */
570*43a90889SApple OSS Distributions 		while ((write_ptr = console_ring_reserve_space(chunk_size)) == NULL) {
571*43a90889SApple OSS Distributions 			if (get_preemption_level() != 0) {
572*43a90889SApple OSS Distributions 				return;
573*43a90889SApple OSS Distributions 			}
574*43a90889SApple OSS Distributions 
575*43a90889SApple OSS Distributions 			console_ring_try_empty(false);
576*43a90889SApple OSS Distributions 		}
577*43a90889SApple OSS Distributions 
578*43a90889SApple OSS Distributions 		for (i = 0; i < chunk_size; i++) {
579*43a90889SApple OSS Distributions 			console_ring_put(&write_ptr, str[i]);
580*43a90889SApple OSS Distributions 		}
581*43a90889SApple OSS Distributions 
582*43a90889SApple OSS Distributions 		console_ring_unreserve_space();
583*43a90889SApple OSS Distributions 		str = &str[i];
584*43a90889SApple OSS Distributions 		size -= chunk_size;
585*43a90889SApple OSS Distributions 	}
586*43a90889SApple OSS Distributions 
587*43a90889SApple OSS Distributions 	/* Do good faith flush if preemption is not disabled */
588*43a90889SApple OSS Distributions 	if (get_preemption_level() == 0) {
589*43a90889SApple OSS Distributions 		console_ring_try_empty(true);
590*43a90889SApple OSS Distributions 	}
591*43a90889SApple OSS Distributions }
592*43a90889SApple OSS Distributions 
593*43a90889SApple OSS Distributions /**
594*43a90889SApple OSS Distributions  * Output a character directly to the underlying console (either video or serial).
595*43a90889SApple OSS Distributions  * This directly bypasses the console serial buffer (as provided by console_write())
596*43a90889SApple OSS Distributions  * and all of the synchronization that provides.
597*43a90889SApple OSS Distributions  *
598*43a90889SApple OSS Distributions  * @note This function can cause serial data to get printed interleaved if being
599*43a90889SApple OSS Distributions  *       called on multiple CPUs at the same time. Only use this function if
600*43a90889SApple OSS Distributions  *       there's a specific reason why this serial data can't get synchronized
601*43a90889SApple OSS Distributions  *       through the console buffer.
602*43a90889SApple OSS Distributions  *
603*43a90889SApple OSS Distributions  * @note disableConsoleOutput is to be used only by core console and init accessors
604*43a90889SApple OSS Distributions  *       such as this function. Returns early if the serial output is disabled.
605*43a90889SApple OSS Distributions  *
606*43a90889SApple OSS Distributions  * @param c The character to print.
607*43a90889SApple OSS Distributions  */
608*43a90889SApple OSS Distributions void
console_write_unbuffered(char c)609*43a90889SApple OSS Distributions console_write_unbuffered(char c)
610*43a90889SApple OSS Distributions {
611*43a90889SApple OSS Distributions 	if (disableConsoleOutput) {
612*43a90889SApple OSS Distributions 		return;
613*43a90889SApple OSS Distributions 	}
614*43a90889SApple OSS Distributions 
615*43a90889SApple OSS Distributions 	_cnputc(c, true);
616*43a90889SApple OSS Distributions }
617*43a90889SApple OSS Distributions 
618*43a90889SApple OSS Distributions /**
619*43a90889SApple OSS Distributions  * Write a single character to the selected console (video or serial).
620*43a90889SApple OSS Distributions  *
621*43a90889SApple OSS Distributions  * @param c The character to print.
622*43a90889SApple OSS Distributions  */
623*43a90889SApple OSS Distributions void
console_write_char(char c)624*43a90889SApple OSS Distributions console_write_char(char c)
625*43a90889SApple OSS Distributions {
626*43a90889SApple OSS Distributions 	console_write(&c, 1);
627*43a90889SApple OSS Distributions }
628*43a90889SApple OSS Distributions 
629*43a90889SApple OSS Distributions /**
630*43a90889SApple OSS Distributions  * Wrapper around the platform-dependent serial input method which handles
631*43a90889SApple OSS Distributions  * waiting for a new character and checking for the NMI string.
632*43a90889SApple OSS Distributions  *
633*43a90889SApple OSS Distributions  * @param wait True if this function should block until a character appears.
634*43a90889SApple OSS Distributions  *
635*43a90889SApple OSS Distributions  * @return The character if one was read, -1 otherwise.
636*43a90889SApple OSS Distributions  */
637*43a90889SApple OSS Distributions int
_serial_getc(bool wait)638*43a90889SApple OSS Distributions _serial_getc(bool wait)
639*43a90889SApple OSS Distributions {
640*43a90889SApple OSS Distributions 	int c = -1;
641*43a90889SApple OSS Distributions 
642*43a90889SApple OSS Distributions 	do {
643*43a90889SApple OSS Distributions 		c = serial_getc();
644*43a90889SApple OSS Distributions 	} while (wait && c < 0);
645*43a90889SApple OSS Distributions 
646*43a90889SApple OSS Distributions 	/* Check for the NMI string. */
647*43a90889SApple OSS Distributions 	if (c == nmi_string[nmi_counter]) {
648*43a90889SApple OSS Distributions 		nmi_counter++;
649*43a90889SApple OSS Distributions 		if (nmi_counter == NMI_STRING_SIZE) {
650*43a90889SApple OSS Distributions 			/* We've got the NMI string, now do an NMI. */
651*43a90889SApple OSS Distributions 			Debugger("Automatic NMI");
652*43a90889SApple OSS Distributions 			nmi_counter = 0;
653*43a90889SApple OSS Distributions 			return '\n';
654*43a90889SApple OSS Distributions 		}
655*43a90889SApple OSS Distributions 	} else if (c != -1) {
656*43a90889SApple OSS Distributions 		nmi_counter = 0;
657*43a90889SApple OSS Distributions 	}
658*43a90889SApple OSS Distributions 
659*43a90889SApple OSS Distributions 	return c;
660*43a90889SApple OSS Distributions }
661*43a90889SApple OSS Distributions 
662*43a90889SApple OSS Distributions /**
663*43a90889SApple OSS Distributions  * Typically the video console doesn't support input, but we call into the
664*43a90889SApple OSS Distributions  * pexpert to give each platform an opportunity to provide console input through
665*43a90889SApple OSS Distributions  * alternative methods if it so desires.
666*43a90889SApple OSS Distributions  *
667*43a90889SApple OSS Distributions  * Usually a platform will either not provide any input, or will grab input from
668*43a90889SApple OSS Distributions  * the serial driver.
669*43a90889SApple OSS Distributions  *
670*43a90889SApple OSS Distributions  * @return The character if one was read, or -1 otherwise.
671*43a90889SApple OSS Distributions  */
672*43a90889SApple OSS Distributions int
_vcgetc(__unused bool wait)673*43a90889SApple OSS Distributions _vcgetc(__unused bool wait)
674*43a90889SApple OSS Distributions {
675*43a90889SApple OSS Distributions 	char c;
676*43a90889SApple OSS Distributions 
677*43a90889SApple OSS Distributions 	if (0 == PE_stub_poll_input(0, &c)) {
678*43a90889SApple OSS Distributions 		return c;
679*43a90889SApple OSS Distributions 	} else {
680*43a90889SApple OSS Distributions 		return -1;
681*43a90889SApple OSS Distributions 	}
682*43a90889SApple OSS Distributions }
683*43a90889SApple OSS Distributions 
684*43a90889SApple OSS Distributions /**
685*43a90889SApple OSS Distributions  * Block until a character is available from the console and return it.
686*43a90889SApple OSS Distributions  *
687*43a90889SApple OSS Distributions  * @return The character retrieved from the console.
688*43a90889SApple OSS Distributions  */
689*43a90889SApple OSS Distributions int
console_read_char(void)690*43a90889SApple OSS Distributions console_read_char(void)
691*43a90889SApple OSS Distributions {
692*43a90889SApple OSS Distributions 	const uint32_t idx = get_cons_ops_index();
693*43a90889SApple OSS Distributions 	return cons_ops[idx].getc(true);
694*43a90889SApple OSS Distributions }
695*43a90889SApple OSS Distributions 
696*43a90889SApple OSS Distributions /**
697*43a90889SApple OSS Distributions  * Attempt to read a character from the console, and if one isn't available,
698*43a90889SApple OSS Distributions  * then return immediately.
699*43a90889SApple OSS Distributions  *
700*43a90889SApple OSS Distributions  * @return The character if one is available, -1 otherwise.
701*43a90889SApple OSS Distributions  */
702*43a90889SApple OSS Distributions int
console_try_read_char(void)703*43a90889SApple OSS Distributions console_try_read_char(void)
704*43a90889SApple OSS Distributions {
705*43a90889SApple OSS Distributions 	const uint32_t idx = get_cons_ops_index();
706*43a90889SApple OSS Distributions 	return cons_ops[idx].getc(false);
707*43a90889SApple OSS Distributions }
708*43a90889SApple OSS Distributions 
709*43a90889SApple OSS Distributions #ifdef CONFIG_XNUPOST
710*43a90889SApple OSS Distributions static uint32_t cons_test_ops_count = 0;
711*43a90889SApple OSS Distributions 
712*43a90889SApple OSS Distributions /*
713*43a90889SApple OSS Distributions  * Log to console by multiple methods - printf, unbuffered write, console_write()
714*43a90889SApple OSS Distributions  */
715*43a90889SApple OSS Distributions static void
log_to_console_func(void * arg __unused,wait_result_t wres __unused)716*43a90889SApple OSS Distributions log_to_console_func(void * arg __unused, wait_result_t wres __unused)
717*43a90889SApple OSS Distributions {
718*43a90889SApple OSS Distributions 	uint64_t thread_id = current_thread()->thread_id;
719*43a90889SApple OSS Distributions 	char somedata[10] = "123456789";
720*43a90889SApple OSS Distributions 	for (int i = 0; i < 26; i++) {
721*43a90889SApple OSS Distributions 		os_atomic_inc(&cons_test_ops_count, relaxed);
722*43a90889SApple OSS Distributions 		printf(" thid: %llu printf iteration %d\n", thread_id, i);
723*43a90889SApple OSS Distributions 		console_write_unbuffered((char)('A' + i));
724*43a90889SApple OSS Distributions 		console_write_unbuffered('\n');
725*43a90889SApple OSS Distributions 		console_write((char *)somedata, sizeof(somedata));
726*43a90889SApple OSS Distributions 		delay(10);
727*43a90889SApple OSS Distributions 	}
728*43a90889SApple OSS Distributions 	printf("finished the log_to_console_func operations\n\n");
729*43a90889SApple OSS Distributions }
730*43a90889SApple OSS Distributions 
731*43a90889SApple OSS Distributions /* Test that outputting to the console can occur on multiple threads at the same time. */
732*43a90889SApple OSS Distributions kern_return_t
console_serial_parallel_log_tests(void)733*43a90889SApple OSS Distributions console_serial_parallel_log_tests(void)
734*43a90889SApple OSS Distributions {
735*43a90889SApple OSS Distributions 	thread_t thread;
736*43a90889SApple OSS Distributions 	kern_return_t kr;
737*43a90889SApple OSS Distributions 	cons_test_ops_count = 0;
738*43a90889SApple OSS Distributions 
739*43a90889SApple OSS Distributions 	kr = kernel_thread_start(log_to_console_func, NULL, &thread);
740*43a90889SApple OSS Distributions 	T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
741*43a90889SApple OSS Distributions 
742*43a90889SApple OSS Distributions 	delay(100);
743*43a90889SApple OSS Distributions 
744*43a90889SApple OSS Distributions 	log_to_console_func(NULL, 0);
745*43a90889SApple OSS Distributions 
746*43a90889SApple OSS Distributions 	/* wait until other thread has also finished */
747*43a90889SApple OSS Distributions 	while (cons_test_ops_count < 52) {
748*43a90889SApple OSS Distributions 		delay(1000);
749*43a90889SApple OSS Distributions 	}
750*43a90889SApple OSS Distributions 
751*43a90889SApple OSS Distributions 	thread_deallocate(thread);
752*43a90889SApple OSS Distributions 	T_LOG("parallel_logging tests is now complete. From this point forward we expect full lines\n");
753*43a90889SApple OSS Distributions 	return KERN_SUCCESS;
754*43a90889SApple OSS Distributions }
755*43a90889SApple OSS Distributions 
756*43a90889SApple OSS Distributions /* Basic serial test that prints serial output through various methods (printf/T_LOG). */
757*43a90889SApple OSS Distributions kern_return_t
console_serial_test(void)758*43a90889SApple OSS Distributions console_serial_test(void)
759*43a90889SApple OSS Distributions {
760*43a90889SApple OSS Distributions 	unsigned long i;
761*43a90889SApple OSS Distributions 	char buffer[CPU_CONS_BUF_SIZE];
762*43a90889SApple OSS Distributions 
763*43a90889SApple OSS Distributions 	T_LOG("Checking console_ring status.");
764*43a90889SApple OSS Distributions 	T_ASSERT_EQ_INT(console_ring.len, KERN_CONSOLE_RING_SIZE, "Console ring size is not correct.");
765*43a90889SApple OSS Distributions 
766*43a90889SApple OSS Distributions 	/* setup buffer to be chars */
767*43a90889SApple OSS Distributions 	for (i = 0; i < CPU_CONS_BUF_SIZE; i++) {
768*43a90889SApple OSS Distributions 		buffer[i] = (char)('0' + (i % 10));
769*43a90889SApple OSS Distributions 	}
770*43a90889SApple OSS Distributions 	buffer[CPU_CONS_BUF_SIZE - 1] = '\0';
771*43a90889SApple OSS Distributions 
772*43a90889SApple OSS Distributions 	T_LOG("Printing %d char string to serial one char at a time.", CPU_CONS_BUF_SIZE);
773*43a90889SApple OSS Distributions 	for (i = 0; i < CPU_CONS_BUF_SIZE; i++) {
774*43a90889SApple OSS Distributions 		printf("%c", buffer[i]);
775*43a90889SApple OSS Distributions 	}
776*43a90889SApple OSS Distributions 	printf("End\n");
777*43a90889SApple OSS Distributions 	T_LOG("Printing %d char string to serial as a whole", CPU_CONS_BUF_SIZE);
778*43a90889SApple OSS Distributions 	printf("%s\n", buffer);
779*43a90889SApple OSS Distributions 
780*43a90889SApple OSS Distributions 	T_LOG("Using console_write call repeatedly for 100 iterations");
781*43a90889SApple OSS Distributions 	for (i = 0; i < 100; i++) {
782*43a90889SApple OSS Distributions 		console_write(&buffer[0], 14);
783*43a90889SApple OSS Distributions 		if ((i % 6) == 0) {
784*43a90889SApple OSS Distributions 			printf("\n");
785*43a90889SApple OSS Distributions 		}
786*43a90889SApple OSS Distributions 	}
787*43a90889SApple OSS Distributions 	printf("\n");
788*43a90889SApple OSS Distributions 
789*43a90889SApple OSS Distributions 	T_LOG("Using T_LOG to print buffer %s", buffer);
790*43a90889SApple OSS Distributions 	return KERN_SUCCESS;
791*43a90889SApple OSS Distributions }
792*43a90889SApple OSS Distributions #endif
793