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 }