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