/ src / console / printk.c
printk.c
  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  
 23  static void console_time_run(void)
 24  {
 25  	if (TRACK_CONSOLE_TIME && boot_cpu())
 26  		timer_monotonic_get(&mt_start);
 27  }
 28  
 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  
 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  
 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  
 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  
 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  
 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  
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  
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  
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  
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  }