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