]> git.saurik.com Git - apple/xnu.git/blob - pexpert/arm/pe_kprintf.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / pexpert / arm / pe_kprintf.c
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 void (*PE_kputc)(char c) = 0;
18
19 SECURITY_READ_ONLY_LATE(unsigned int) disable_serial_output = TRUE;
20
21 decl_simple_lock_data(static, kprintf_lock);
22
23 static void serial_putc_crlf(char c);
24
25 void
26 PE_init_kprintf(boolean_t vm_initialized)
27 {
28 unsigned int boot_arg;
29
30 if (PE_state.initialized == FALSE) {
31 panic("Platform Expert not initialized");
32 }
33
34 if (!vm_initialized) {
35 simple_lock_init(&kprintf_lock, 0);
36
37 if (PE_parse_boot_argn("debug", &boot_arg, sizeof(boot_arg))) {
38 if (boot_arg & DB_KPRT) {
39 disable_serial_output = FALSE;
40 }
41 }
42
43 if (serial_init()) {
44 PE_kputc = serial_putc_crlf;
45 } else {
46 PE_kputc = cnputc;
47 }
48 }
49 }
50
51 #ifdef MP_DEBUG
52 static void
53 _kprintf(const char *format, ...)
54 {
55 va_list listp;
56
57 va_start(listp, format);
58 _doprnt_log(format, &listp, PE_kputc, 16);
59 va_end(listp);
60 }
61 #define MP_DEBUG_KPRINTF(x...) _kprintf(x)
62 #else /* MP_DEBUG */
63 #define MP_DEBUG_KPRINTF(x...)
64 #endif /* MP_DEBUG */
65
66 #if CONFIG_NO_KPRINTF_STRINGS
67 /* Prevent CPP from breaking the definition below */
68 #undef kprintf
69 #endif
70
71 static int cpu_last_locked = 0;
72
73 __attribute__((noinline, not_tail_called))
74 void
75 kprintf(const char *fmt, ...)
76 {
77 va_list listp;
78 va_list listp2;
79 boolean_t state;
80 void *caller = __builtin_return_address(0);
81
82 if (!disable_serial_output) {
83 /*
84 * Spin to get kprintf lock but re-enable interrupts while failing.
85 * This allows interrupts to be handled while waiting but
86 * interrupts are disabled once we have the lock.
87 */
88 state = ml_set_interrupts_enabled(FALSE);
89 while (!simple_lock_try(&kprintf_lock, LCK_GRP_NULL)) {
90 ml_set_interrupts_enabled(state);
91 ml_set_interrupts_enabled(FALSE);
92 }
93
94 if (cpu_number() != cpu_last_locked) {
95 MP_DEBUG_KPRINTF("[cpu%d...]\n", cpu_number());
96 cpu_last_locked = cpu_number();
97 }
98
99 va_start(listp, fmt);
100 va_copy(listp2, listp);
101 _doprnt_log(fmt, &listp, PE_kputc, 16);
102 va_end(listp);
103
104 simple_unlock(&kprintf_lock);
105
106 #if INTERRUPT_MASKED_DEBUG
107 /*
108 * kprintf holds interrupts disabled for far too long
109 * and would trip the spin-debugger. If we are about to reenable
110 * interrupts then clear the timer and avoid panicking on the delay.
111 * Otherwise, let the code that printed with interrupt disabled
112 * take the panic when it reenables interrupts.
113 * Hopefully one day this is fixed so that this workaround is unnecessary.
114 */
115 if (state == TRUE) {
116 ml_spin_debug_clear_self();
117 }
118 #endif
119 ml_set_interrupts_enabled(state);
120
121 // If interrupts are enabled
122 if (ml_get_interrupts_enabled()) {
123 os_log_with_args(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, fmt, listp2, caller);
124 }
125 va_end(listp2);
126 } else {
127 // If interrupts are enabled
128 if (ml_get_interrupts_enabled()) {
129 va_start(listp, fmt);
130 os_log_with_args(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, fmt, listp, caller);
131 va_end(listp);
132 }
133 }
134 }
135
136 static void
137 serial_putc_crlf(char c)
138 {
139 if (c == '\n') {
140 uart_putc('\r');
141 }
142 uart_putc(c);
143 }
144
145 void
146 serial_putc(char c)
147 {
148 uart_putc(c);
149 }
150
151 int
152 serial_getc(void)
153 {
154 return uart_getc();
155 }