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