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