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 */
17void (*PE_kputc) (char c) = 0;
18
19SECURITY_READ_ONLY_LATE(unsigned int) disable_serial_output = TRUE;
20
21decl_simple_lock_data(static, kprintf_lock)
22
23void
24PE_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
46static 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
65static int cpu_last_locked = 0;
66
67__attribute__((noinline,not_tail_called))
68void 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
130void
131serial_putc(char c)
132{
133 uart_putc(c);
134}
135
136int
137serial_getc(void)
138{
139 return uart_getc();
140}
141
142