2 * Copyright (c) 2000-2019 Apple, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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__ */
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 <libkern/section_keywords.h>
49 #if __arm__ || __arm64__
50 #include <machine/machine_routines.h>
51 #include <arm/cpu_data_internal.h>
55 #include <tests/xnupost.h>
56 kern_return_t
console_serial_test(void);
57 kern_return_t
console_serial_alloc_rel_tests(void);
58 kern_return_t
console_serial_parallel_log_tests(void);
59 #define MAX_CPU_SLOTS (MAX_CPUS + 2)
63 #define MAX_CPU_SLOTS (MAX_CPUS)
73 decl_simple_lock_data(, read_lock
);
74 lck_ticket_t write_lock
;
78 * New allocation mechanism for console buffers
79 * Total allocation: 1 * PAGE_SIZE
80 * - Each cpu gets CPU_CONS_BUF_SIZE buffer
81 * - Kernel wide console ring gets PAGE_SIZE - MAX_CPU_SLOTS * CPU_CONS_BUF_SIZE
83 * At the return from console_init() the memory is setup as follows:
84 * +----------------------------+-------------+-------------+-------------+-------------+
85 * |console ring buffer---------|f2eec075-----|f2eec075-----|f2eec075-----|f2eec075-----|
86 * +----------------------------+-------------+-------------+-------------+-------------+
87 * Each cpu allocation will find the first (f2eec075) and use that buffer.
91 #define CPU_CONS_BUF_SIZE 256
92 #define CPU_BUF_FREE_HEX 0xf2eec075
94 #define KERN_CONSOLE_BUF_SIZE vm_map_round_page(CPU_CONS_BUF_SIZE *(MAX_CPU_SLOTS + 1), PAGE_SIZE - 1)
95 #define KERN_CONSOLE_RING_SIZE (KERN_CONSOLE_BUF_SIZE - (CPU_CONS_BUF_SIZE * MAX_CPU_SLOTS))
98 * A serial line running at 115200 bps can output ~11.5 characters per millisecond.
99 * Synchronous serial logging with preemption disabled fundamentally prevents us
100 * from hitting expected scheduling deadlines, but we can at least tone it down a bit.
102 * TODO: IOLog should use asynchronous serial logging instead of the synchronous serial console. (26555148)
104 * Keep interrupt disabled periods shorter than 1ms
106 #define MAX_NO_PREEMPT_FLUSH_SIZE 8
107 #define MAX_TOTAL_FLUSH_SIZE (MAX(2, MAX_CPU_SLOTS) * CPU_CONS_BUF_SIZE)
109 typedef struct console_buf
{
113 #define CPU_BUFFER_LEN (CPU_CONS_BUF_SIZE - 3 * (sizeof(char *)))
114 char buf
[CPU_BUFFER_LEN
];
117 extern int serial_getc(void);
118 extern void serial_putc(char);
120 #if DEBUG || DEVELOPMENT
121 TUNABLE(bool, allow_printf_from_interrupts_disabled_context
, "nointr_consio", false);
123 #define allow_printf_from_interrupts_disabled_context false
126 static void _serial_putc(int, int, int);
128 SECURITY_READ_ONLY_EARLY(struct console_ops
) cons_ops
[] = {
130 .putc
= _serial_putc
, .getc
= _serial_getc
,
133 .putc
= vcputc
, .getc
= vcgetc
,
137 SECURITY_READ_ONLY_EARLY(uint32_t) nconsops
= (sizeof cons_ops
/ sizeof cons_ops
[0]);
140 uint32_t cons_ops_index
= VC_CONS_OPS
;
142 SECURITY_READ_ONLY_LATE(uint32_t) cons_ops_index
= VC_CONS_OPS
;
145 LCK_GRP_DECLARE(console_lck_grp
, "console");
147 // NMI static variables
148 #define NMI_STRING_SIZE 32
149 char nmi_string
[NMI_STRING_SIZE
] = "afDIGHr84A84jh19Kphgp428DNPdnapq";
150 static int nmi_counter
= 0;
152 static bool console_suspended
= false;
155 console_io_allowed(void)
157 if (!allow_printf_from_interrupts_disabled_context
&&
158 !console_suspended
&&
159 startup_phase
>= STARTUP_SUB_EARLY_BOOT
&&
160 !ml_get_interrupts_enabled()) {
161 #if defined(__arm__) || defined(__arm64__) || DEBUG || DEVELOPMENT
162 panic("Console I/O from interrupt-disabled context");
172 console_ring_lock_init(void)
174 simple_lock_init(&console_ring
.read_lock
, 0);
175 lck_ticket_init(&console_ring
.write_lock
, &console_lck_grp
);
184 if (!OSCompareAndSwap(0, KERN_CONSOLE_RING_SIZE
, (UInt32
*)&console_ring
.len
)) {
188 assert(console_ring
.len
> 0);
190 ret
= kmem_alloc_flags(kernel_map
, (vm_offset_t
*)&console_ring
.buffer
,
191 KERN_CONSOLE_BUF_SIZE
+ 2 * PAGE_SIZE
, VM_KERN_MEMORY_OSFMK
,
192 KMA_KOBJECT
| KMA_PERMANENT
| KMA_GUARD_FIRST
| KMA_GUARD_LAST
);
193 if (ret
!= KERN_SUCCESS
) {
194 panic("console_ring_init() failed to allocate ring buffer, error %d\n", ret
);
197 console_ring
.buffer
+= PAGE_SIZE
;
199 /* setup memory for per cpu console buffers */
200 for (i
= 0; i
< MAX_CPU_SLOTS
; i
++) {
201 p
= (uint32_t *)((uintptr_t)console_ring
.buffer
+ console_ring
.len
+ (i
* sizeof(console_buf_t
)));
202 *p
= CPU_BUF_FREE_HEX
;
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 console_ring_lock_init();
213 console_cpu_alloc(__unused boolean_t boot_processor
)
220 assert(console_ring
.buffer
!= NULL
);
222 /* select the next slot from the per cpu buffers at end of console_ring.buffer */
223 for (i
= 0; i
< MAX_CPU_SLOTS
; i
++) {
224 p
= (uint32_t *)((uintptr_t)console_ring
.buffer
+ console_ring
.len
+ (i
* sizeof(console_buf_t
)));
225 if (OSCompareAndSwap(CPU_BUF_FREE_HEX
, 0, (UInt32
*)p
)) {
229 assert(i
< MAX_CPU_SLOTS
);
231 cbp
= (console_buf_t
*)(uintptr_t)p
;
232 if ((uintptr_t)cbp
>= (uintptr_t)console_ring
.buffer
+ KERN_CONSOLE_BUF_SIZE
) {
233 printf("console_cpu_alloc() failed to allocate cpu buffer\n");
237 cbp
->buf_base
= (char *)&cbp
->buf
;
238 cbp
->buf_ptr
= cbp
->buf_base
;
239 cbp
->buf_end
= cbp
->buf_base
+ CPU_BUFFER_LEN
;
244 console_cpu_free(void * buf
)
246 assert((uintptr_t)buf
> (uintptr_t)console_ring
.buffer
);
247 assert((uintptr_t)buf
< (uintptr_t)console_ring
.buffer
+ KERN_CONSOLE_BUF_SIZE
);
249 *(uint32_t *)buf
= CPU_BUF_FREE_HEX
;
254 console_ring_reserve_space(int nchars
)
256 char *write_ptr
= NULL
;
257 lck_ticket_lock(&console_ring
.write_lock
, &console_lck_grp
);
258 if ((console_ring
.len
- console_ring
.used
) >= nchars
) {
259 console_ring
.used
+= nchars
;
260 mp_disable_preemption(); // Don't allow preemption while holding a reservation; otherwise console_ring_try_empty() could take arbitrarily long
261 os_atomic_inc(&console_ring
.nreserved
, relaxed
);
262 write_ptr
= console_ring
.write_ptr
;
263 console_ring
.write_ptr
=
264 console_ring
.buffer
+ ((console_ring
.write_ptr
- console_ring
.buffer
+ nchars
) % console_ring
.len
);
266 lck_ticket_unlock(&console_ring
.write_lock
);
271 console_ring_unreserve_space(void)
273 os_atomic_dec(&console_ring
.nreserved
, relaxed
);
274 mp_enable_preemption();
278 console_ring_put(char **write_ptr
, char ch
)
280 assert(console_ring
.nreserved
> 0);
283 if ((*write_ptr
- console_ring
.buffer
) == console_ring
.len
) {
284 *write_ptr
= console_ring
.buffer
;
288 static inline boolean_t
289 cpu_buffer_put(console_buf_t
* cbp
, char ch
)
291 if (ch
!= '\0' && cbp
->buf_ptr
< cbp
->buf_end
) {
292 *(cbp
->buf_ptr
++) = ch
;
300 cpu_buffer_size(console_buf_t
* cbp
)
302 return (int)(cbp
->buf_ptr
- cbp
->buf_base
);
305 static inline uint32_t
306 get_cons_ops_index(void)
308 uint32_t idx
= cons_ops_index
;
310 if (idx
>= nconsops
) {
311 panic("Bad cons_ops_index: %d", idx
);
318 _cnputs(char * c
, int size
)
320 extern int disableConsoleOutput
;
322 if (disableConsoleOutput
) {
328 const uint32_t idx
= get_cons_ops_index();
332 cons_ops
[idx
].putc(0, 0, '\r');
334 cons_ops
[idx
].putc(0, 0, *c
);
340 cnputc_unbuffered(char c
)
353 cnputsusr(char *s
, int size
)
355 console_write(s
, size
);
359 console_ring_try_empty(void)
361 char flush_buf
[MAX_NO_PREEMPT_FLUSH_SIZE
];
364 int total_chars_out
= 0;
365 int size_before_wrap
= 0;
366 bool in_debugger
= (kernel_debugger_entry_count
> 0);
369 if (__improbable(!console_io_allowed())) {
375 * Try to get the read lock on the ring buffer to empty it.
376 * If this fails someone else is already emptying...
378 if (!in_debugger
&& !simple_lock_try(&console_ring
.read_lock
, &console_lck_grp
)) {
382 if (__probable(!in_debugger
)) {
383 lck_ticket_lock(&console_ring
.write_lock
, &console_lck_grp
);
384 while (os_atomic_load(&console_ring
.nreserved
, relaxed
) > 0) {
389 /* try small chunk at a time, so we allow writes from other cpus into the buffer */
390 nchars_out
= MIN(console_ring
.used
, (int)sizeof(flush_buf
));
392 /* account for data to be read before wrap around */
393 size_before_wrap
= (int)((console_ring
.buffer
+ console_ring
.len
) - console_ring
.read_ptr
);
394 if (nchars_out
> size_before_wrap
) {
395 nchars_out
= size_before_wrap
;
398 if (nchars_out
> 0) {
399 memcpy(flush_buf
, console_ring
.read_ptr
, nchars_out
);
400 console_ring
.read_ptr
=
401 console_ring
.buffer
+ ((console_ring
.read_ptr
- console_ring
.buffer
+ nchars_out
) % console_ring
.len
);
402 console_ring
.used
-= nchars_out
;
405 if (__probable(!in_debugger
)) {
406 lck_ticket_unlock(&console_ring
.write_lock
);
409 if (nchars_out
> 0) {
410 total_chars_out
+= nchars_out
;
411 _cnputs(flush_buf
, nchars_out
);
414 if (__probable(!in_debugger
)) {
415 simple_unlock(&console_ring
.read_lock
);
419 * In case we end up being the console drain thread
420 * for far too long, break out. Except in panic/suspend cases
421 * where we should clear out full buffer.
423 if (!console_suspended
&& (total_chars_out
>= MAX_TOTAL_FLUSH_SIZE
)) {
426 } while (nchars_out
> 0);
433 console_suspended
= true;
434 console_ring_try_empty();
440 console_suspended
= false;
444 console_write(char * str
, int size
)
448 int chunk_size
= CPU_CONS_BUF_SIZE
;
451 if (__improbable(!console_io_allowed())) {
453 } else if (__improbable(console_suspended
)) {
455 * Put directly to console if we're heading into suspend or if we're in
456 * the kernel debugger for a panic/stackshot. If any of the other cores
457 * happened to halt while holding any of the console locks, attempting
458 * to use the normal path will result in sadness.
465 if (size
< chunk_size
) {
468 while ((write_ptr
= console_ring_reserve_space(chunk_size
)) == NULL
) {
469 console_ring_try_empty();
472 for (i
= 0; i
< chunk_size
; i
++) {
473 console_ring_put(&write_ptr
, str
[i
]);
476 console_ring_unreserve_space();
481 console_ring_try_empty();
488 cpu_data_t
* cpu_data_p
;
489 boolean_t needs_print
= TRUE
;
494 mp_disable_preemption();
495 cpu_data_p
= current_cpu_datap();
496 cbp
= (console_buf_t
*)cpu_data_p
->cpu_console_buf
;
497 if (console_suspended
|| cbp
== NULL
) {
498 mp_enable_preemption();
500 * Put directly if console ring is not initialized or we're heading into suspend.
501 * Also do this if we're in the kernel debugger for a panic or stackshot.
502 * If any of the other cores happened to halt while holding any of the console
503 * locks, attempting to use the normal path will result in sadness.
511 * If the cpu buffer is full, we'll flush, then try
512 * another put. If it fails a second time... screw
515 if (needs_print
&& !cpu_buffer_put(cbp
, c
)) {
516 write_ptr
= console_ring_reserve_space(cpu_buffer_size(cbp
));
517 if (write_ptr
== NULL
) {
518 mp_enable_preemption();
520 console_ring_try_empty();
524 for (cp
= cbp
->buf_base
; cp
< cbp
->buf_ptr
; cp
++) {
525 console_ring_put(&write_ptr
, *cp
);
527 console_ring_unreserve_space();
529 cbp
->buf_ptr
= cbp
->buf_base
;
530 cpu_buffer_put(cbp
, c
);
536 mp_enable_preemption();
540 /* We printed a newline, time to flush the CPU buffer to the global buffer */
543 * Is there enough space in the shared ring buffer?
544 * Try to empty if not.
545 * Note, we want the entire local buffer to fit to
546 * avoid another cpu interjecting.
549 write_ptr
= console_ring_reserve_space(cpu_buffer_size(cbp
));
550 if (write_ptr
== NULL
) {
551 mp_enable_preemption();
553 console_ring_try_empty();
557 for (cp
= cbp
->buf_base
; cp
< cbp
->buf_ptr
; cp
++) {
558 console_ring_put(&write_ptr
, *cp
);
561 console_ring_unreserve_space();
562 cbp
->buf_ptr
= cbp
->buf_base
;
564 mp_enable_preemption();
566 console_ring_try_empty();
572 _serial_getc(__unused
int a
, __unused
int b
, boolean_t wait
, __unused boolean_t raw
)
577 } while (wait
&& c
< 0);
579 // Check for the NMI string
580 if (c
== nmi_string
[nmi_counter
]) {
582 if (nmi_counter
== NMI_STRING_SIZE
) {
583 // We've got the NMI string, now do an NMI
584 Debugger("Automatic NMI");
588 } else if (c
!= -1) {
596 _serial_putc(__unused
int a
, __unused
int b
, int c
)
598 serial_putc((char)c
);
604 uint32_t idx
= get_cons_ops_index();
605 return cons_ops
[idx
].getc(0, 0, TRUE
, FALSE
);
611 uint32_t idx
= get_cons_ops_index();
612 return cons_ops
[idx
].getc(0, 0, FALSE
, FALSE
);
616 vcgetc(__unused
int l
, __unused
int u
, __unused boolean_t wait
, __unused boolean_t raw
)
620 if (0 == PE_stub_poll_input(0, &c
)) {
627 #ifdef CONFIG_XNUPOST
628 static uint32_t cons_test_ops_count
= 0;
631 * Try to do multiple cpu buffer allocs and free and intentionally
632 * allow for pre-emption.
635 alloc_free_func(void * arg
, wait_result_t wres __unused
)
637 console_buf_t
* cbp
= NULL
;
638 int count
= (int)arg
;
640 T_LOG("Doing %d iterations of console cpu alloc and free.", count
);
642 while (count
-- > 0) {
643 os_atomic_inc(&cons_test_ops_count
, relaxed
);
644 cbp
= (console_buf_t
*)console_cpu_alloc(0);
646 T_ASSERT_NOTNULL(cbp
, "cpu allocation failed");
648 console_cpu_free(cbp
);
650 /* give chance to another thread to come in */
656 * Log to console by multiple methods - printf, unbuffered write, console_write()
659 log_to_console_func(void * arg __unused
, wait_result_t wres __unused
)
661 uint64_t thread_id
= current_thread()->thread_id
;
662 char somedata
[10] = "123456789";
663 for (int i
= 0; i
< 26; i
++) {
664 os_atomic_inc(&cons_test_ops_count
, relaxed
);
665 printf(" thid: %llu printf iteration %d\n", thread_id
, i
);
666 cnputc_unbuffered((char)('A' + i
));
667 cnputc_unbuffered('\n');
668 console_write((char *)somedata
, sizeof(somedata
));
671 printf("finished the log_to_console_func operations\n\n");
675 console_serial_parallel_log_tests(void)
679 cons_test_ops_count
= 0;
681 kr
= kernel_thread_start(log_to_console_func
, NULL
, &thread
);
682 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
686 log_to_console_func(NULL
, 0);
688 /* wait until other thread has also finished */
689 while (cons_test_ops_count
< 52) {
693 thread_deallocate(thread
);
694 T_LOG("parallel_logging tests is now complete. From this point forward we expect full lines\n");
699 console_serial_alloc_rel_tests(void)
701 unsigned long i
, free_buf_count
= 0;
707 T_LOG("doing alloc/release tests");
709 for (i
= 0; i
< MAX_CPU_SLOTS
; i
++) {
710 p
= (uint32_t *)((uintptr_t)console_ring
.buffer
+ console_ring
.len
+ (i
* sizeof(console_buf_t
)));
711 cbp
= (console_buf_t
*)(void *)p
;
712 /* p should either be allocated cpu buffer or have CPU_BUF_FREE_HEX in it */
713 T_ASSERT(*p
== CPU_BUF_FREE_HEX
|| cbp
->buf_base
== &cbp
->buf
[0], "");
714 if (*p
== CPU_BUF_FREE_HEX
) {
719 T_ASSERT_GE_ULONG(free_buf_count
, 2, "At least 2 buffers should be free");
720 cons_test_ops_count
= 0;
722 kr
= kernel_thread_start(alloc_free_func
, (void *)1000, &thread
);
723 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
725 /* yeild cpu to give other thread chance to get on-core */
728 alloc_free_func((void *)1000, 0);
730 /* wait until other thread finishes its tasks */
731 while (cons_test_ops_count
< 2000) {
735 thread_deallocate(thread
);
736 /* verify again that atleast 2 slots are free */
738 for (i
= 0; i
< MAX_CPU_SLOTS
; i
++) {
739 p
= (uint32_t *)((uintptr_t)console_ring
.buffer
+ console_ring
.len
+ (i
* sizeof(console_buf_t
)));
740 cbp
= (console_buf_t
*)(void *)p
;
741 /* p should either be allocated cpu buffer or have CPU_BUF_FREE_HEX in it */
742 T_ASSERT(*p
== CPU_BUF_FREE_HEX
|| cbp
->buf_base
== &cbp
->buf
[0], "");
743 if (*p
== CPU_BUF_FREE_HEX
) {
747 T_ASSERT_GE_ULONG(free_buf_count
, 2, "At least 2 buffers should be free after alloc free tests");
753 console_serial_test(void)
756 char buffer
[CPU_BUFFER_LEN
];
760 T_LOG("Checking console_ring status.");
761 T_ASSERT_EQ_INT(console_ring
.len
, KERN_CONSOLE_RING_SIZE
, "Console ring size is not correct.");
762 T_ASSERT_GT_INT(KERN_CONSOLE_BUF_SIZE
, KERN_CONSOLE_RING_SIZE
, "kernel console buffer size is < allocation.");
764 /* select the next slot from the per cpu buffers at end of console_ring.buffer */
765 for (i
= 0; i
< MAX_CPU_SLOTS
; i
++) {
766 p
= (uint32_t *)((uintptr_t)console_ring
.buffer
+ console_ring
.len
+ (i
* sizeof(console_buf_t
)));
767 cbp
= (console_buf_t
*)(void *)p
;
768 /* p should either be allocated cpu buffer or have CPU_BUF_FREE_HEX in it */
769 T_ASSERT(*p
== CPU_BUF_FREE_HEX
|| cbp
->buf_base
== &cbp
->buf
[0], "verified initialization of cpu buffers p=%p", (void *)p
);
772 /* setup buffer to be chars */
773 for (i
= 0; i
< CPU_BUFFER_LEN
; i
++) {
774 buffer
[i
] = (char)('0' + (i
% 10));
776 buffer
[CPU_BUFFER_LEN
- 1] = '\0';
778 T_LOG("Printing %d char string to serial one char at a time.", CPU_BUFFER_LEN
);
779 for (i
= 0; i
< CPU_BUFFER_LEN
; i
++) {
780 printf("%c", buffer
[i
]);
783 T_LOG("Printing %d char string to serial as a whole", CPU_BUFFER_LEN
);
784 printf("%s\n", buffer
);
786 T_LOG("Using console_write call repeatedly for 100 iterations");
787 for (i
= 0; i
< 100; i
++) {
788 console_write(&buffer
[0], 14);
795 T_LOG("Using T_LOG to print buffer %s", buffer
);