xref: /xnu-8020.140.41/pexpert/arm/pe_kprintf.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
1 /*
2  * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
3  */
4 /*
5  * file: pe_kprintf.c
6  *    arm platform expert debugging output initialization.
7  */
8 #include <stdarg.h>
9 #include <machine/machine_routines.h>
10 #include <pexpert/pexpert.h>
11 #include <kern/debug.h>
12 #include <kern/simple_lock.h>
13 #include <os/log_private.h>
14 #include <libkern/section_keywords.h>
15 
16 /* Globals */
17 typedef void (*PE_kputc_t)(char);
18 SECURITY_READ_ONLY_LATE(PE_kputc_t) PE_kputc;
19 
20 // disable_serial_output disables kprintf() *and* unbuffered panic output.
21 SECURITY_READ_ONLY_LATE(bool) disable_serial_output = true;
22 // disable_kprintf_output only disables kprintf().
23 SECURITY_READ_ONLY_LATE(bool) disable_kprintf_output = true;
24 // disable_iolog_serial_output only disables IOLog, controlled by
25 // SERIALMODE_NO_IOLOG.
26 SECURITY_READ_ONLY_LATE(bool) disable_iolog_serial_output = false;
27 
28 static SIMPLE_LOCK_DECLARE(kprintf_lock, 0);
29 
30 static void serial_putc_crlf(char c);
31 
32 __startup_func
33 static void
PE_init_kprintf_config(void)34 PE_init_kprintf_config(void)
35 {
36 	if (PE_state.initialized == FALSE) {
37 		panic("Platform Expert not initialized");
38 	}
39 
40 	if (debug_boot_arg & DB_KPRT) {
41 		disable_serial_output = false;
42 	}
43 
44 #if DEBUG
45 	disable_kprintf_output = false;
46 #elif DEVELOPMENT
47 	bool enable_kprintf_spam = false;
48 	if (PE_parse_boot_argn("-enable_kprintf_spam", &enable_kprintf_spam, sizeof(enable_kprintf_spam))) {
49 		disable_kprintf_output = false;
50 	}
51 #endif
52 }
53 // Do this early, so other code can depend on whether kprintf is enabled.
54 STARTUP(TUNABLES, STARTUP_RANK_LAST, PE_init_kprintf_config);
55 
56 __startup_func
57 static void
PE_init_kprintf(void)58 PE_init_kprintf(void)
59 {
60 	if (serial_init()) {
61 		PE_kputc = serial_putc_crlf;
62 	} else {
63 		/**
64 		 * If serial failed to initialize then fall back to using the console,
65 		 * and assume the console is using the video console (because clearly
66 		 * serial doesn't work).
67 		 */
68 		PE_kputc = console_write_unbuffered;
69 	}
70 }
71 STARTUP(KPRINTF, STARTUP_RANK_FIRST, PE_init_kprintf);
72 
73 #ifdef MP_DEBUG
74 static void
_kprintf(const char * format,...)75 _kprintf(const char *format, ...)
76 {
77 	va_list         listp;
78 
79 	va_start(listp, format);
80 	_doprnt_log(format, &listp, PE_kputc, 16);
81 	va_end(listp);
82 }
83 #define MP_DEBUG_KPRINTF(x...)  _kprintf(x)
84 #else                           /* MP_DEBUG */
85 #define MP_DEBUG_KPRINTF(x...)
86 #endif                          /* MP_DEBUG */
87 
88 #if CONFIG_NO_KPRINTF_STRINGS
89 /* Prevent CPP from breaking the definition below */
90 #undef kprintf
91 #endif
92 
93 static int      cpu_last_locked = 0;
94 
95 __attribute__((noinline, not_tail_called))
96 void
kprintf(const char * fmt,...)97 kprintf(const char *fmt, ...)
98 {
99 	va_list         listp;
100 	va_list         listp2;
101 	boolean_t       state;
102 	void           *caller = __builtin_return_address(0);
103 
104 	if (!disable_serial_output && !disable_kprintf_output) {
105 		va_start(listp, fmt);
106 		va_copy(listp2, listp);
107 		/*
108 		 * Spin to get kprintf lock but re-enable interrupts while failing.
109 		 * This allows interrupts to be handled while waiting but
110 		 * interrupts are disabled once we have the lock.
111 		 */
112 		state = ml_set_interrupts_enabled(FALSE);
113 		while (!simple_lock_try(&kprintf_lock, LCK_GRP_NULL)) {
114 			ml_set_interrupts_enabled(state);
115 			ml_set_interrupts_enabled(FALSE);
116 		}
117 
118 		if (cpu_number() != cpu_last_locked) {
119 			MP_DEBUG_KPRINTF("[cpu%d...]\n", cpu_number());
120 			cpu_last_locked = cpu_number();
121 		}
122 
123 		_doprnt_log(fmt, &listp, PE_kputc, 16);
124 
125 		simple_unlock(&kprintf_lock);
126 
127 #if INTERRUPT_MASKED_DEBUG
128 		/*
129 		 * kprintf holds interrupts disabled for far too long
130 		 * and would trip the spin-debugger.  If we are about to reenable
131 		 * interrupts then clear the timer and avoid panicking on the delay.
132 		 * Otherwise, let the code that printed with interrupt disabled
133 		 * take the panic when it reenables interrupts.
134 		 * Hopefully one day this is fixed so that this workaround is unnecessary.
135 		 */
136 		if (state == TRUE) {
137 			ml_spin_debug_clear_self();
138 		}
139 #endif
140 		ml_set_interrupts_enabled(state);
141 		va_end(listp);
142 
143 #pragma clang diagnostic push
144 #pragma clang diagnostic ignored "-Wformat-nonliteral"
145 		os_log_with_args(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, fmt, listp2, caller);
146 		va_end(listp2);
147 	} else {
148 		va_start(listp, fmt);
149 		os_log_with_args(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, fmt, listp, caller);
150 		va_end(listp);
151 	}
152 #pragma clang diagnostic pop
153 }
154 
155 static void
serial_putc_crlf(char c)156 serial_putc_crlf(char c)
157 {
158 	if (c == '\n') {
159 		uart_putc('\r');
160 	}
161 	uart_putc(c);
162 }
163 
164 void
serial_putc_options(char c,bool poll)165 serial_putc_options(char c, bool poll)
166 {
167 	uart_putc_options(c, poll);
168 }
169 
170 void
serial_putc(char c)171 serial_putc(char c)
172 {
173 	uart_putc(c);
174 }
175 
176 int
serial_getc(void)177 serial_getc(void)
178 {
179 	return uart_getc();
180 }
181