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 = kernel_memory_allocate(kernel_map,
196 (vm_offset_t *)&console_ring.buffer,
197 KERN_CONSOLE_RING_SIZE + (2 * PAGE_SIZE), 0,
198 KMA_KOBJECT | KMA_PERMANENT | KMA_GUARD_FIRST | KMA_GUARD_LAST,
199 VM_KERN_MEMORY_OSFMK);
200 if (ret != KERN_SUCCESS) {
201 panic("console_ring_init() failed to allocate ring buffer, error %d", ret);
202 }
203
204 console_ring.buffer += PAGE_SIZE; /* Skip past the first guard page. */
205 console_ring.used = 0;
206 console_ring.nreserved = 0;
207 console_ring.read_ptr = console_ring.buffer;
208 console_ring.write_ptr = console_ring.buffer;
209
210 lck_mtx_init(&console_ring.flush_lock, &console_lck_grp, LCK_ATTR_NULL);
211 lck_ticket_init(&console_ring.write_lock, &console_lck_grp);
212 }
213
214 /**
215 * Returns true when the console has already been initialized.
216 */
217 static inline bool
is_console_initialized(void)218 is_console_initialized(void)
219 {
220 return console_ring.len == KERN_CONSOLE_RING_SIZE;
221 }
222
223 /**
224 * Return the index to the currently selected console (serial/video). This is
225 * an index into the "cons_ops[]" array of function pointer structs.
226 */
227 static inline uint32_t
get_cons_ops_index(void)228 get_cons_ops_index(void)
229 {
230 uint32_t idx = cons_ops_index;
231
232 if (idx >= nconsops) {
233 panic("Bad cons_ops_index: %d", idx);
234 }
235
236 return idx;
237 }
238
239 /**
240 * Helper function for outputting a character to the underlying console
241 * (either video or serial) with the possibility of sleeping waiting for
242 * an interrupt indicating the console is ready.
243 *
244 * @note assumes console_ring.read lock is held if poll == false
245 *
246 * @param c The character to print.
247 * @param poll Whether or not this call should poll instead of going to sleep
248 * waiting for an interrupt when the hardware device isn't ready
249 */
250 static inline void
_cnputc(char c,bool poll)251 _cnputc(char c, bool poll)
252 {
253 bool in_debugger = (kernel_debugger_entry_count > 0);
254 const uint32_t idx = get_cons_ops_index();
255
256 poll = poll || in_debugger;
257
258 if (c == '\n') {
259 _cnputc('\r', poll);
260 }
261
262 cons_ops[idx].putc(c, poll);
263 }
264
265 /**
266 * Helper function for outputting characters directly to the underlying console
267 * (either video or serial).
268 *
269 * @param c The array of characters to print.
270 * @param poll Whether or not this call should poll instead of going to sleep
271 * waiting for an interrupt when the hardware device isn't ready
272 * @param size The number of characters to print to the console.
273 */
274 static inline void
_cnputs(char * c,int size,bool poll)275 _cnputs(char *c, int size, bool poll)
276 {
277 extern int disableConsoleOutput;
278
279 if (disableConsoleOutput) {
280 return;
281 }
282
283 assert(c != NULL);
284
285 while (size-- > 0) {
286 _cnputc(*c, poll);
287 c++;
288 }
289 }
290
291 /**
292 * Attempt to reserve space for a number of characters in the console ring
293 * buffer. Space in the ring buffer must be reserved before new characters can
294 * be entered.
295 *
296 * Every call to this function should be paired with a corresponding call to
297 * console_ring_unreserve_space().
298 *
299 * @note If space is successfully reserved, this will disable preemption because
300 * otherwise, console_ring_try_empty() could take arbitrarily long.
301 *
302 * @param nchars The number of characters to reserve.
303 *
304 * @return If the wanted number of characters could not be reserved, then return
305 * NULL. Otherwise, return a pointer to the beginning of the reserved
306 * space.
307 */
308 static inline char*
console_ring_reserve_space(int nchars)309 console_ring_reserve_space(int nchars)
310 {
311 char *write_ptr = NULL;
312 lck_ticket_lock(&console_ring.write_lock, &console_lck_grp);
313 if ((console_ring.len - console_ring.used) >= nchars) {
314 console_ring.used += nchars;
315 mp_disable_preemption();
316 os_atomic_inc(&console_ring.nreserved, relaxed);
317
318 /* Return out the pointer to the beginning of the just reserved space. */
319 write_ptr = console_ring.write_ptr;
320
321 /* Move the console ring's write pointer to the beginning of the next free space. */
322 const ptrdiff_t write_index = console_ring.write_ptr - console_ring.buffer;
323 console_ring.write_ptr = console_ring.buffer + ((write_index + nchars) % console_ring.len);
324 }
325 lck_ticket_unlock(&console_ring.write_lock);
326 return write_ptr;
327 }
328
329 /**
330 * Decrement the number of reserved spaces in the console ring (now that the data
331 * has been written) and re-enable preemption.
332 *
333 * Every call to this function should be paired with a corresponding call to
334 * console_ring_reserve_space().
335 */
336 static inline void
console_ring_unreserve_space(void)337 console_ring_unreserve_space(void)
338 {
339 assert(console_ring.nreserved > 0);
340
341 os_atomic_dec(&console_ring.nreserved, relaxed);
342 mp_enable_preemption();
343 }
344
345 /**
346 * Write a single character into the console ring buffer and handle moving the
347 * write pointer circularly around the buffer.
348 *
349 * @note Space to write this character must have already been reserved using
350 * console_ring_reserve_space().
351 *
352 * @param write_ptr Pointer into the reserved space in the buffer to write the
353 * character. This pointer will get moved to the next valid
354 * location to write a character so the same pointer can be
355 * passed into subsequent calls to write multiple characters.
356 * @param ch The character to insert into the ring buffer.
357 */
358 static inline void
console_ring_put(char ** write_ptr,char ch)359 console_ring_put(char **write_ptr, char ch)
360 {
361 assert(console_ring.nreserved > 0);
362 **write_ptr = ch;
363 ++(*write_ptr);
364 if ((*write_ptr - console_ring.buffer) == console_ring.len) {
365 *write_ptr = console_ring.buffer;
366 }
367 }
368
369 /**
370 * Attempt to drain the console ring buffer if no other CPUs are already doing
371 * so.
372 *
373 * @param fail_fast If true, this function returns immediately instead of
374 * sleeping if the thread fails to acquire the console flush
375 * mutex.
376 *
377 * @note This function should not be called with preemption disabled.
378 *
379 * @note To prevent one CPU from holding the console lock for too long, only
380 * MAX_FLUSH_SIZE_LOCK_HELD number of characters can be drained at a time
381 * with the lock held. The lock will be dropped between each drain of size
382 * MAX_FLUSH_SIZE_LOCK_HELD to allow another CPU to grab the lock. If
383 * another CPU grabs the lock, then the original thread can stop draining
384 * and return instead of sleeping for the lock.
385 *
386 * @note To prevent one thread from being the drain thread for too long (presumably
387 * that thread has other things it wants to do besides draining serial), the
388 * total number of characters a single call to this function can drain is
389 * restricted to MAX_TOTAL_FLUSH_SIZE.
390 */
391 static void
console_ring_try_empty(bool fail_fast)392 console_ring_try_empty(bool fail_fast)
393 {
394 char flush_buf[MAX_FLUSH_SIZE_LOCK_HELD];
395
396 int nchars_out = 0;
397 int total_chars_out = 0;
398 int size_before_wrap = 0;
399 bool in_debugger = (kernel_debugger_entry_count > 0);
400
401 if (__improbable(!console_io_allowed()) || get_preemption_level() != 0) {
402 return;
403 }
404
405 do {
406 if (__probable(!in_debugger) && fail_fast && !lck_mtx_try_lock(&console_ring.flush_lock)) {
407 return;
408 } else if (__probable(!in_debugger) && !fail_fast) {
409 lck_mtx_lock(&console_ring.flush_lock);
410 }
411
412 if (__probable(!in_debugger)) {
413 lck_ticket_lock(&console_ring.write_lock, &console_lck_grp);
414
415 /**
416 * If we've managed to grab the write lock, but there's still space
417 * reserved in the buffer, then other CPUs are actively writing into
418 * the ring, wait for them to finish.
419 */
420 while (os_atomic_load(&console_ring.nreserved, relaxed) > 0) {
421 cpu_pause();
422 }
423 }
424
425 /* Try small chunk at a time, so we allow writes from other cpus into the buffer. */
426 nchars_out = MIN(console_ring.used, (int)sizeof(flush_buf));
427
428 /* Account for data to be read before wrap around. */
429 size_before_wrap = (int)((console_ring.buffer + console_ring.len) - console_ring.read_ptr);
430 if (nchars_out > size_before_wrap) {
431 nchars_out = size_before_wrap;
432 }
433
434 /**
435 * Copy the characters to be drained into a separate flush buffer, and
436 * move the console read pointer to the next chunk of data that needs to
437 * be drained.
438 */
439 if (nchars_out > 0) {
440 memcpy(flush_buf, console_ring.read_ptr, nchars_out);
441 const ptrdiff_t read_index = console_ring.read_ptr - console_ring.buffer;
442 console_ring.read_ptr = console_ring.buffer + ((read_index + nchars_out) % console_ring.len);
443 console_ring.used -= nchars_out;
444 }
445
446 if (__probable(!in_debugger)) {
447 lck_ticket_unlock(&console_ring.write_lock);
448 }
449
450 /**
451 * Output characters to the underlying console (serial/video). We should
452 * only poll if the console is suspended.
453 */
454 if (nchars_out > 0) {
455 total_chars_out += nchars_out;
456 _cnputs(flush_buf, nchars_out, console_suspended);
457 }
458
459 if (__probable(!in_debugger)) {
460 lck_mtx_unlock(&console_ring.flush_lock);
461 }
462
463 /**
464 * Prevent this thread from sleeping on the lock again if another thread
465 * grabs it after we drop it.
466 */
467 fail_fast = true;
468
469 /*
470 * In case we end up being the console drain thread for far too long,
471 * break out. Except in panic/suspend cases where we should clear out
472 * the full buffer.
473 */
474 if (!console_suspended && (total_chars_out >= MAX_TOTAL_FLUSH_SIZE)) {
475 break;
476 }
477 } while (nchars_out > 0);
478 }
479
480 /**
481 * Notify the console subystem that all following console writes should skip
482 * synchronization and get outputted directly to the underlying console. This is
483 * important for cases like panic/stackshots and going down for sleep where
484 * assumptions about the state of the system could cause hangs or nested panics.
485 */
486 void
console_suspend()487 console_suspend()
488 {
489 console_suspended = true;
490 console_ring_try_empty(false);
491 }
492
493 /**
494 * Notify the console subsystem that it is now safe to use the console ring
495 * buffer synchronization when writing console data.
496 */
497 void
console_resume()498 console_resume()
499 {
500 console_suspended = false;
501 }
502
503 /**
504 * Write a string of characters to the underlying video or serial console in a
505 * synchronized manner. By synchronizing access to a global console buffer, this
506 * prevents the serial output from appearing interleaved to the end user when
507 * multiple CPUs are outputting to the console at the same time.
508 *
509 * @note It's safe to call this function even before the console buffer has been
510 * initialized. In that case, the data will be sent directly to the
511 * underlying console with no buffering. This is the same for when the
512 * console is suspended.
513 *
514 * @param str The string of characters to print.
515 * @param size The number of characters in `str` to print.
516 */
517 void
console_write(char * str,int size)518 console_write(char *str, int size)
519 {
520 assert(str != NULL);
521
522 char *write_ptr = NULL;
523 int chunk_size = CPU_CONS_BUF_SIZE;
524 int i = 0;
525
526 if (__improbable(console_suspended || !is_console_initialized() || pmap_in_ppl())) {
527 /*
528 * Output directly to console in the following cases:
529 * 1. If this is early in boot before the console has been initialized.
530 * 2. If we're heading into suspend.
531 * 3. If we're in the kernel debugger for a panic/stackshot. If any of
532 * the other cores happened to halt while holding any of the console
533 * locks, attempting to use the normal path will result in sadness.
534 * 4. If we're in the PPL. As we synchronize the ring buffer with a
535 * mutex and preemption is disabled in the PPL, any writes must go
536 * directly to the hardware device.
537 */
538 _cnputs(str, size, true);
539 return;
540 } else if (__improbable(!console_io_allowed())) {
541 return;
542 }
543
544 while (size > 0) {
545 /**
546 * Restrict the maximum number of characters that can be reserved at
547 * once. This helps prevent one CPU from reserving too much and starving
548 * out the other CPUs.
549 */
550 if (size < chunk_size) {
551 chunk_size = size;
552 }
553
554 /**
555 * Attempt to reserve space in the ring buffer and if that fails, then
556 * keep attempting to drain the ring buffer until there's enough space.
557 * We can't flush the serial console with preemption disabled so return
558 * early to drop the message in that case.
559 */
560 while ((write_ptr = console_ring_reserve_space(chunk_size)) == NULL) {
561 if (get_preemption_level() != 0) {
562 return;
563 }
564
565 console_ring_try_empty(false);
566 }
567
568 for (i = 0; i < chunk_size; i++) {
569 console_ring_put(&write_ptr, str[i]);
570 }
571
572 console_ring_unreserve_space();
573 str = &str[i];
574 size -= chunk_size;
575 }
576
577 /* Do good faith flush if preemption is not disabled */
578 if (get_preemption_level() == 0) {
579 console_ring_try_empty(true);
580 }
581 }
582
583 /**
584 * Output a character directly to the underlying console (either video or serial).
585 * This directly bypasses the console serial buffer (as provided by console_write())
586 * and all of the synchronization that provides.
587 *
588 * @note This function can cause serial data to get printed interleaved if being
589 * called on multiple CPUs at the same time. Only use this function if
590 * there's a specific reason why this serial data can't get synchronized
591 * through the console buffer.
592 *
593 * @param c The character to print.
594 */
595 void
console_write_unbuffered(char c)596 console_write_unbuffered(char c)
597 {
598 _cnputc(c, true);
599 }
600
601 /**
602 * Write a single character to the selected console (video or serial).
603 *
604 * @param c The character to print.
605 */
606 void
console_write_char(char c)607 console_write_char(char c)
608 {
609 console_write(&c, 1);
610 }
611
612 /**
613 * Wrapper around the platform-dependent serial input method which handles
614 * waiting for a new character and checking for the NMI string.
615 *
616 * @param wait True if this function should block until a character appears.
617 *
618 * @return The character if one was read, -1 otherwise.
619 */
620 int
_serial_getc(bool wait)621 _serial_getc(bool wait)
622 {
623 int c = -1;
624
625 do {
626 c = serial_getc();
627 } while (wait && c < 0);
628
629 /* Check for the NMI string. */
630 if (c == nmi_string[nmi_counter]) {
631 nmi_counter++;
632 if (nmi_counter == NMI_STRING_SIZE) {
633 /* We've got the NMI string, now do an NMI. */
634 Debugger("Automatic NMI");
635 nmi_counter = 0;
636 return '\n';
637 }
638 } else if (c != -1) {
639 nmi_counter = 0;
640 }
641
642 return c;
643 }
644
645 /**
646 * Typically the video console doesn't support input, but we call into the
647 * pexpert to give each platform an opportunity to provide console input through
648 * alternative methods if it so desires.
649 *
650 * Usually a platform will either not provide any input, or will grab input from
651 * the serial driver.
652 *
653 * @return The character if one was read, or -1 otherwise.
654 */
655 int
_vcgetc(__unused bool wait)656 _vcgetc(__unused bool wait)
657 {
658 char c;
659
660 if (0 == PE_stub_poll_input(0, &c)) {
661 return c;
662 } else {
663 return -1;
664 }
665 }
666
667 /**
668 * Block until a character is available from the console and return it.
669 *
670 * @return The character retrieved from the console.
671 */
672 int
console_read_char(void)673 console_read_char(void)
674 {
675 const uint32_t idx = get_cons_ops_index();
676 return cons_ops[idx].getc(true);
677 }
678
679 /**
680 * Attempt to read a character from the console, and if one isn't available,
681 * then return immediately.
682 *
683 * @return The character if one is available, -1 otherwise.
684 */
685 int
console_try_read_char(void)686 console_try_read_char(void)
687 {
688 const uint32_t idx = get_cons_ops_index();
689 return cons_ops[idx].getc(false);
690 }
691
692 #ifdef CONFIG_XNUPOST
693 static uint32_t cons_test_ops_count = 0;
694
695 /*
696 * Log to console by multiple methods - printf, unbuffered write, console_write()
697 */
698 static void
log_to_console_func(void * arg __unused,wait_result_t wres __unused)699 log_to_console_func(void * arg __unused, wait_result_t wres __unused)
700 {
701 uint64_t thread_id = current_thread()->thread_id;
702 char somedata[10] = "123456789";
703 for (int i = 0; i < 26; i++) {
704 os_atomic_inc(&cons_test_ops_count, relaxed);
705 printf(" thid: %llu printf iteration %d\n", thread_id, i);
706 console_write_unbuffered((char)('A' + i));
707 console_write_unbuffered('\n');
708 console_write((char *)somedata, sizeof(somedata));
709 delay(10);
710 }
711 printf("finished the log_to_console_func operations\n\n");
712 }
713
714 /* Test that outputting to the console can occur on multiple threads at the same time. */
715 kern_return_t
console_serial_parallel_log_tests(void)716 console_serial_parallel_log_tests(void)
717 {
718 thread_t thread;
719 kern_return_t kr;
720 cons_test_ops_count = 0;
721
722 kr = kernel_thread_start(log_to_console_func, NULL, &thread);
723 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
724
725 delay(100);
726
727 log_to_console_func(NULL, 0);
728
729 /* wait until other thread has also finished */
730 while (cons_test_ops_count < 52) {
731 delay(1000);
732 }
733
734 thread_deallocate(thread);
735 T_LOG("parallel_logging tests is now complete. From this point forward we expect full lines\n");
736 return KERN_SUCCESS;
737 }
738
739 /* Basic serial test that prints serial output through various methods (printf/T_LOG). */
740 kern_return_t
console_serial_test(void)741 console_serial_test(void)
742 {
743 unsigned long i;
744 char buffer[CPU_CONS_BUF_SIZE];
745
746 T_LOG("Checking console_ring status.");
747 T_ASSERT_EQ_INT(console_ring.len, KERN_CONSOLE_RING_SIZE, "Console ring size is not correct.");
748
749 /* setup buffer to be chars */
750 for (i = 0; i < CPU_CONS_BUF_SIZE; i++) {
751 buffer[i] = (char)('0' + (i % 10));
752 }
753 buffer[CPU_CONS_BUF_SIZE - 1] = '\0';
754
755 T_LOG("Printing %d char string to serial one char at a time.", CPU_CONS_BUF_SIZE);
756 for (i = 0; i < CPU_CONS_BUF_SIZE; i++) {
757 printf("%c", buffer[i]);
758 }
759 printf("End\n");
760 T_LOG("Printing %d char string to serial as a whole", CPU_CONS_BUF_SIZE);
761 printf("%s\n", buffer);
762
763 T_LOG("Using console_write call repeatedly for 100 iterations");
764 for (i = 0; i < 100; i++) {
765 console_write(&buffer[0], 14);
766 if ((i % 6) == 0) {
767 printf("\n");
768 }
769 }
770 printf("\n");
771
772 T_LOG("Using T_LOG to print buffer %s", buffer);
773 return KERN_SUCCESS;
774 }
775 #endif
776