xref: /aosp_15_r20/external/coreboot/src/console/printk.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * blatantly copied from linux/kernel/printk.c
5  */
6 
7 #include <console/cbmem_console.h>
8 #include <console/console.h>
9 #include <console/streams.h>
10 #include <console/vtxprintf.h>
11 #include <smp/spinlock.h>
12 #include <smp/node.h>
13 #include <timer.h>
14 #include <types.h>
15 
16 DECLARE_SPIN_LOCK(console_lock)
17 
18 #define TRACK_CONSOLE_TIME (!ENV_SMM && CONFIG(HAVE_MONOTONIC_TIMER))
19 
20 static struct mono_time mt_start, mt_stop;
21 static long console_usecs;
22 
console_time_run(void)23 static void console_time_run(void)
24 {
25 	if (TRACK_CONSOLE_TIME && boot_cpu())
26 		timer_monotonic_get(&mt_start);
27 }
28 
console_time_stop(void)29 static void console_time_stop(void)
30 {
31 	if (TRACK_CONSOLE_TIME && boot_cpu()) {
32 		timer_monotonic_get(&mt_stop);
33 		console_usecs += mono_time_diff_microseconds(&mt_start, &mt_stop);
34 	}
35 }
36 
console_time_report(void)37 void console_time_report(void)
38 {
39 	if (!TRACK_CONSOLE_TIME)
40 		return;
41 
42 	printk(BIOS_DEBUG, "BS: " ENV_STRING " times (exec / console): total (unknown) / %ld ms\n",
43 		DIV_ROUND_CLOSEST(console_usecs, USECS_PER_MSEC));
44 }
45 
console_time_get_and_reset(void)46 long console_time_get_and_reset(void)
47 {
48 	if (!TRACK_CONSOLE_TIME)
49 		return 0;
50 
51 	long elapsed = console_usecs;
52 	console_usecs = 0;
53 	return elapsed;
54 }
55 
do_putchar(unsigned char byte)56 void do_putchar(unsigned char byte)
57 {
58 	console_time_run();
59 	console_tx_byte(byte);
60 	console_time_stop();
61 }
62 
63 union log_state {
64 	void *as_ptr;
65 	struct {
66 		uint8_t level;
67 		uint8_t speed;
68 	};
69 };
70 
71 #define LOG_FAST(state) (HAS_ONLY_FAST_CONSOLES || ((state).speed == CONSOLE_LOG_FAST))
72 
wrap_interactive_printf(const char * fmt,...)73 static void wrap_interactive_printf(const char *fmt, ...)
74 {
75 	va_list args;
76 	va_start(args, fmt);
77 	vtxprintf(console_interactive_tx_byte, fmt, args, NULL);
78 	va_end(args);
79 }
80 
line_start(union log_state state)81 static void line_start(union log_state state)
82 {
83 	if (state.level > BIOS_LOG_PREFIX_MAX_LEVEL)
84 		return;
85 
86 	/* Stored consoles just get a single control char marker to save space. If we are in
87 	   LOG_FAST mode, just write the marker to CBMC and exit -- the rest of this function
88 	   implements the LOG_ALL case. */
89 	unsigned char marker = BIOS_LOG_LEVEL_TO_MARKER(state.level);
90 	if (LOG_FAST(state)) {
91 		__cbmemc_tx_byte(marker);
92 		return;
93 	}
94 	console_stored_tx_byte(marker, NULL);
95 
96 	/* Interactive consoles get a `[DEBUG]  ` style readable prefix,
97 	   and potentially an escape sequence for highlighting. */
98 	if (CONFIG(CONSOLE_USE_ANSI_ESCAPES))
99 		wrap_interactive_printf(BIOS_LOG_ESCAPE_PATTERN, bios_log_escape[state.level]);
100 	if (CONFIG(CONSOLE_USE_LOGLEVEL_PREFIX))
101 		wrap_interactive_printf(BIOS_LOG_PREFIX_PATTERN, bios_log_prefix[state.level]);
102 }
103 
line_end(union log_state state)104 static void line_end(union log_state state)
105 {
106 	if (CONFIG(CONSOLE_USE_ANSI_ESCAPES) && !LOG_FAST(state))
107 		wrap_interactive_printf(BIOS_LOG_ESCAPE_RESET);
108 }
109 
wrap_putchar(unsigned char byte,void * data)110 static void wrap_putchar(unsigned char byte, void *data)
111 {
112 	union log_state state = { .as_ptr = data };
113 	static bool line_started = false;
114 
115 	if (byte == '\n') {
116 		line_end(state);
117 		line_started = false;
118 	} else if (!line_started) {
119 		line_start(state);
120 		line_started = true;
121 	}
122 
123 	if (LOG_FAST(state))
124 		__cbmemc_tx_byte(byte);
125 	else
126 		console_tx_byte(byte);
127 }
128 
vprintk(int msg_level,const char * fmt,va_list args)129 int vprintk(int msg_level, const char *fmt, va_list args)
130 {
131 	union log_state state = { .level = msg_level };
132 	int i;
133 
134 	if (CONFIG(SQUELCH_EARLY_SMP) && ENV_ROMSTAGE_OR_BEFORE && !boot_cpu())
135 		return 0;
136 
137 	state.speed = console_log_level(msg_level);
138 	if (state.speed < CONSOLE_LOG_FAST)
139 		return 0;
140 
141 	spin_lock(&console_lock);
142 
143 	console_time_run();
144 
145 	i = vtxprintf(wrap_putchar, fmt, args, state.as_ptr);
146 	if (LOG_FAST(state))
147 		console_tx_flush();
148 
149 	console_time_stop();
150 
151 	spin_unlock(&console_lock);
152 
153 	return i;
154 }
155 
printk(int msg_level,const char * fmt,...)156 int printk(int msg_level, const char *fmt, ...)
157 {
158 	va_list args;
159 	int i;
160 
161 	va_start(args, fmt);
162 	i = vprintk(msg_level, fmt, args);
163 	va_end(args);
164 
165 	return i;
166 }
167