1#include <asm.h>
2#include <global.h>
3#include <limine.h>
4#include <limits.h>
5#include <logo.h>
6#include <stdarg.h>
7#include <stdbool.h>
8#include <stdint.h>
9#include <string.h>
10#include <sync/spinlock.h>
11
12#include "console/printf.h"
13#include "flanterm/src/flanterm.h"
14#include <flanterm/src/flanterm_backends/fb.h>
15
16#undef snprintf
17#undef vsnprintf
18
19void vprintf(struct printf_cursor *csr, const char *format, va_list args);
20struct flanterm_context;
21
22/* The panic console may deliberately bypass normal ownership and ordering. */
23struct spinlock k_printf_lock = SPINLOCK_INIT_CHK(NULL, LOCK_UNCHKD);
24struct flanterm_context *ft_ctx;
25
26struct printf_cursor {
27 char *buffer;
28 int buffer_len;
29 int cursor;
30};
31
32void serial_init(void) {
33 outb(port: 0x3F8 + 1, value: 0x00);
34 outb(port: 0x3F8 + 3, value: 0x80);
35 outb(port: 0x3F8 + 0, value: 0x03);
36 outb(port: 0x3F8 + 1, value: 0x00);
37 outb(port: 0x3F8 + 3, value: 0x03);
38 outb(port: 0x3F8 + 2, value: 0xC7);
39 outb(port: 0x3F8 + 4, value: 0x0B);
40 for (volatile int i = 0; i < 1000; i++)
41 cpu_pause();
42}
43
44static int serial_is_transmit_empty(void) {
45 return inb(port: 0x3F8 + 5) & 0x20;
46}
47
48static void serial_putc(char c) {
49 while (serial_is_transmit_empty() == 0)
50 ;
51 outb(port: 0x3F8, value: (uint8_t) c);
52}
53
54void serial_write(const char *const str cc_pass_object_size(0), size_t len) {
55 for (size_t i = 0; i < len; i++)
56 serial_putc(c: str[i]);
57}
58
59bool serial_try_getc(char *out) {
60 if (!(inb(port: 0x3F8 + 5) & 1))
61 return false;
62
63 *out = (char) inb(port: 0x3F8);
64 return true;
65}
66
67static void serial_puts(struct printf_cursor *csr, const char *str, int len) {
68 for (int i = 0; i < len; i++) {
69 if (!csr)
70 serial_putc(c: str[i]);
71 if (csr && csr->cursor < csr->buffer_len - 1) {
72 if (csr->buffer)
73 csr->buffer[csr->cursor] = str[i];
74 csr->cursor++;
75 }
76 }
77}
78
79static void double_print(struct flanterm_context *f, struct printf_cursor *csr,
80 const char *str, int len) {
81 serial_puts(csr, str, len);
82
83 if (!csr && global.current_bootstage >= BOOTSTAGE_EARLY_FB)
84 flanterm_write(ctx: f, buf: str, count: len);
85}
86
87void printf_init(struct limine_framebuffer *fb) {
88 serial_init();
89 ft_ctx = flanterm_fb_init(
90 NULL, NULL, framebuffer: fb->address, width: fb->width, height: fb->height, pitch: fb->pitch,
91 red_mask_size: fb->red_mask_size, red_mask_shift: fb->red_mask_shift, green_mask_size: fb->green_mask_size,
92 green_mask_shift: fb->green_mask_shift, blue_mask_size: fb->blue_mask_size, blue_mask_shift: fb->blue_mask_shift, NULL,
93 NULL, NULL, NULL, NULL, NULL, NULL, NULL, font_width: 0, font_height: 0, font_spacing: 1, font_scale_x: 0, font_scale_y: 0, margin: 0, rotation: 0, autoflush: 0);
94 printf(format: "%s", OS_LOGO_SMALL);
95}
96
97static int print_signed(char *buffer, int64_t num) {
98 int neg = 0;
99 int n = 0;
100 if (num < 0) {
101 neg = 1;
102 num = -num;
103 }
104 do {
105 buffer[n++] = '0' + (num % 10);
106 num /= 10;
107 } while (num > 0);
108 for (int i = 0; i < n / 2; i++) {
109 char tmp = buffer[i];
110 buffer[i] = buffer[n - 1 - i];
111 buffer[n - 1 - i] = tmp;
112 }
113 if (neg) {
114 memmove(buffer + 1, buffer, n);
115 buffer[0] = '-';
116 n++;
117 }
118 return n;
119}
120
121static int print_unsigned(char *buffer, uint64_t num) {
122 int n = 0;
123 do {
124 buffer[n++] = '0' + (num % 10);
125 num /= 10;
126 } while (num > 0);
127 for (int i = 0; i < n / 2; i++) {
128 char tmp = buffer[i];
129 buffer[i] = buffer[n - 1 - i];
130 buffer[n - 1 - i] = tmp;
131 }
132 return n;
133}
134
135static int print_hex(char *buffer, uint64_t num) {
136 const char *digits = "0123456789abcdef";
137 int n = 0;
138 do {
139 buffer[n++] = digits[num % 16];
140 num /= 16;
141 } while (num > 0);
142 for (int i = 0; i < n / 2; i++) {
143 char tmp = buffer[i];
144 buffer[i] = buffer[n - 1 - i];
145 buffer[n - 1 - i] = tmp;
146 }
147 return n;
148}
149
150static int print_hex_upper(char *buffer, uint64_t num) {
151 const char *digits = "0123456789ABCDEF";
152 int n = 0;
153 do {
154 buffer[n++] = digits[num % 16];
155 num /= 16;
156 } while (num > 0);
157 for (int i = 0; i < n / 2; i++) {
158 char tmp = buffer[i];
159 buffer[i] = buffer[n - 1 - i];
160 buffer[n - 1 - i] = tmp;
161 }
162 return n;
163}
164
165static int print_binary(char *buffer, uint64_t num) {
166 int n = 0;
167 if (num == 0) {
168 buffer[n++] = '0';
169 } else {
170 while (num > 0) {
171 buffer[n++] = (num % 2) ? '1' : '0';
172 num /= 2;
173 }
174 for (int i = 0; i < n / 2; i++) {
175 char tmp = buffer[i];
176 buffer[i] = buffer[n - 1 - i];
177 buffer[n - 1 - i] = tmp;
178 }
179 }
180 return n;
181}
182
183static int print_octal(char *buffer, uint64_t num) {
184 const char *digits = "01234567";
185 int n = 0;
186 if (num == 0) {
187 buffer[n++] = '0';
188 return n;
189 }
190 while (num > 0) {
191 buffer[n++] = digits[num % 8];
192 num /= 8;
193 }
194 for (int i = 0; i < n / 2; i++) {
195 char tmp = buffer[i];
196 buffer[i] = buffer[n - 1 - i];
197 buffer[n - 1 - i] = tmp;
198 }
199 return n;
200}
201
202static int print_fixed32(char *buffer, uint64_t raw, int precision) {
203 int32_t n = 0;
204 int32_t int_part = (int32_t) (raw >> 32);
205 uint32_t frac_bits = (uint32_t) (raw & 0xFFFFFFFFULL);
206
207 if (int_part < 0) {
208 if (frac_bits != 0) {
209 frac_bits = (uint32_t) (0x100000000ULL - (uint64_t) frac_bits);
210 int_part = -int_part - 1;
211 } else {
212 int_part = -int_part;
213 }
214 buffer[n++] = '-';
215 }
216
217 uint32_t uint_int = (uint32_t) int_part;
218 char int_buf[12];
219 int32_t int_len = 0;
220 do {
221 int_buf[int_len++] = '0' + (uint_int % 10);
222 uint_int /= 10;
223 } while (uint_int > 0);
224
225 for (int32_t i = 0; i < int_len / 2; i++) {
226 char tmp = int_buf[i];
227 int_buf[i] = int_buf[int_len - 1 - i];
228 int_buf[int_len - 1 - i] = tmp;
229 }
230
231 for (int32_t i = 0; i < int_len; i++)
232 buffer[n++] = int_buf[i];
233
234 if (precision == 0)
235 return n;
236
237 if (precision > 10)
238 precision = 10;
239
240 buffer[n++] = '.';
241
242 uint64_t frac = (uint64_t) frac_bits;
243 for (int32_t i = 0; i < precision; i++) {
244 frac *= 10;
245 buffer[n++] = '0' + (int) (frac >> 32);
246 frac &= 0xFFFFFFFFULL;
247 }
248
249 return n;
250}
251
252static void apply_padding(const char *str, int len, int width, bool left_align,
253 bool zero_pad, struct printf_cursor *csr) {
254 if (len >= width) {
255 double_print(f: ft_ctx, csr, str, len);
256 return;
257 }
258 int padding = width - len;
259 char pad_char = zero_pad ? '0' : ' ';
260 if (!left_align) {
261 if (zero_pad && len > 0 && (str[0] == '-' || str[0] == '+')) {
262 double_print(f: ft_ctx, csr, str, len: 1);
263 for (int i = 0; i < padding; i++)
264 double_print(f: ft_ctx, csr, str: &pad_char, len: 1);
265 double_print(f: ft_ctx, csr, str: str + 1, len: len - 1);
266 } else {
267 for (int i = 0; i < padding; i++)
268 double_print(f: ft_ctx, csr, str: &pad_char, len: 1);
269 double_print(f: ft_ctx, csr, str, len);
270 }
271 } else {
272 double_print(f: ft_ctx, csr, str, len);
273 for (int i = 0; i < padding; i++)
274 double_print(f: ft_ctx, csr, str: " ", len: 1);
275 }
276}
277
278static void handle_format_specifier(struct printf_cursor *csr,
279 const char **format_ptr, va_list args) {
280 const char *format = *format_ptr;
281 bool left_align = false;
282 bool zero_pad = false;
283 int width = 0;
284 int precision = -1;
285
286 while (*format == '-' || *format == '+' || *format == '0' ||
287 *format == ' ' || *format == '#') {
288 if (*format == '-')
289 left_align = true;
290 else if (*format == '0')
291 zero_pad = true;
292 format++;
293 }
294
295 if (*format == '*') {
296 width = va_arg(args, int);
297 if (width < 0) {
298 left_align = true;
299 width = -width;
300 }
301 format++;
302 } else {
303 while (*format >= '0' && *format <= '9') {
304 width = width * 10 + (*format - '0');
305 format++;
306 }
307 }
308
309 if (*format == '.') {
310 format++;
311 precision = 0;
312 while (*format >= '0' && *format <= '9') {
313 precision = precision * 10 + (*format - '0');
314 format++;
315 }
316 }
317
318 enum { LEN_NONE, LEN_HH, LEN_H, LEN_L, LEN_LL, LEN_Z } len_mod = LEN_NONE;
319 if (*format == 'z') {
320 len_mod = LEN_Z;
321 format++;
322 } else if (*format == 'h') {
323 format++;
324 if (*format == 'h') {
325 len_mod = LEN_HH;
326 format++;
327 } else {
328 len_mod = LEN_H;
329 }
330 } else if (*format == 'l') {
331 format++;
332 if (*format == 'l') {
333 len_mod = LEN_LL;
334 format++;
335 } else {
336 len_mod = LEN_L;
337 }
338 }
339
340 char spec = *format++;
341 char buffer[64];
342 int len = 0;
343
344 switch (spec) {
345 case 'd':
346 case 'i': {
347 int64_t num;
348 switch (len_mod) {
349 case LEN_HH: num = (signed char) va_arg(args, int); break;
350 case LEN_H: num = (short) va_arg(args, int); break;
351 case LEN_L: num = va_arg(args, long); break;
352 case LEN_LL: num = va_arg(args, long long); break;
353 case LEN_Z: num = (int64_t) va_arg(args, uint64_t); break;
354 default: num = va_arg(args, int); break;
355 }
356 len = print_signed(buffer, num);
357 break;
358 }
359 case 'u': {
360 uint64_t num;
361 switch (len_mod) {
362 case LEN_HH: num = (unsigned char) va_arg(args, unsigned int); break;
363 case LEN_H: num = (unsigned short) va_arg(args, unsigned int); break;
364 case LEN_L: num = va_arg(args, unsigned long); break;
365 case LEN_LL: num = va_arg(args, unsigned long long); break;
366 case LEN_Z: num = va_arg(args, uint64_t); break;
367 default: num = va_arg(args, unsigned int); break;
368 }
369 len = print_unsigned(buffer, num);
370 break;
371 }
372 case 'x': {
373 uint64_t num;
374 switch (len_mod) {
375 case LEN_HH: num = (unsigned char) va_arg(args, unsigned int); break;
376 case LEN_H: num = (unsigned short) va_arg(args, unsigned int); break;
377 case LEN_L: num = va_arg(args, unsigned long); break;
378 case LEN_LL: num = va_arg(args, unsigned long long); break;
379 case LEN_Z: num = va_arg(args, uint64_t); break;
380 default: num = va_arg(args, unsigned int); break;
381 }
382 len = print_hex(buffer, num);
383 break;
384 }
385 case 'X': {
386 uint64_t num;
387 switch (len_mod) {
388 case LEN_HH: num = (unsigned char) va_arg(args, unsigned int); break;
389 case LEN_H: num = (unsigned short) va_arg(args, unsigned int); break;
390 case LEN_L: num = va_arg(args, unsigned long); break;
391 case LEN_LL: num = va_arg(args, unsigned long long); break;
392 case LEN_Z: num = va_arg(args, uint64_t); break;
393 default: num = va_arg(args, unsigned int); break;
394 }
395 len = print_hex_upper(buffer, num);
396 break;
397 }
398 case 'b': {
399 uint64_t num;
400 switch (len_mod) {
401 case LEN_HH: num = (unsigned char) va_arg(args, unsigned int); break;
402 case LEN_H: num = (unsigned short) va_arg(args, unsigned int); break;
403 case LEN_L: num = va_arg(args, unsigned long); break;
404 case LEN_LL: num = va_arg(args, unsigned long long); break;
405 case LEN_Z: num = va_arg(args, uint64_t); break;
406 default: num = va_arg(args, unsigned int); break;
407 }
408 len = print_binary(buffer, num);
409 break;
410 }
411 case 'o': {
412 uint64_t num;
413 switch (len_mod) {
414 case LEN_HH: num = (unsigned char) va_arg(args, unsigned int); break;
415 case LEN_H: num = (unsigned short) va_arg(args, unsigned int); break;
416 case LEN_L: num = va_arg(args, unsigned long); break;
417 case LEN_LL: num = va_arg(args, unsigned long long); break;
418 case LEN_Z: num = va_arg(args, uint64_t); break;
419 default: num = va_arg(args, unsigned int); break;
420 }
421 len = print_octal(buffer, num);
422 break;
423 }
424 case 'p': {
425 uintptr_t num = (uintptr_t) va_arg(args, void *);
426 if (num == 0) {
427 apply_padding(str: "(nil)", len: 5, width, left_align, false, csr);
428 *format_ptr = format;
429 return;
430 }
431 buffer[0] = '0';
432 buffer[1] = 'x';
433 len = 2 + print_hex(buffer: buffer + 2, num);
434 zero_pad = false;
435 break;
436 }
437 case 's': {
438 char *str = va_arg(args, char *);
439 if (!str)
440 str = "(null)";
441 len = strlen(str);
442 if (precision >= 0 && precision < len)
443 len = precision;
444 apply_padding(str, len, width, left_align, false, csr);
445 *format_ptr = format;
446 return;
447 }
448 case 'c': {
449 buffer[0] = (char) va_arg(args, int);
450 len = 1;
451 zero_pad = false;
452 break;
453 }
454 case 'F': {
455 uint64_t raw = va_arg(args, uint64_t);
456 int prec = (precision >= 0) ? precision : 6;
457 len = print_fixed32(buffer, raw, precision: prec);
458 break;
459 }
460 case '%': {
461 buffer[0] = '%';
462 len = 1;
463 zero_pad = false;
464 break;
465 }
466 default: {
467 buffer[0] = '%';
468 buffer[1] = spec;
469 len = 2;
470 zero_pad = false;
471 break;
472 }
473 }
474
475 apply_padding(str: buffer, len, width, left_align, zero_pad, csr);
476 *format_ptr = format;
477}
478
479void vprintf(struct printf_cursor *csr, const char *format, va_list args) {
480 while (*format) {
481 if (*format == '%') {
482 format++;
483 handle_format_specifier(csr, format_ptr: &format, args);
484 } else {
485 double_print(f: ft_ctx, csr, str: format, len: 1);
486 format++;
487 }
488 }
489}
490
491enum irql printf_lock(void) TSA_ACQUIRES_SPIN(&k_printf_lock) {
492 return spin_lock_high(&k_printf_lock);
493}
494
495void printf_unlock(enum irql i) TSA_RELEASES_SPIN(&k_printf_lock) {
496 spin_unlock(&k_printf_lock, i);
497}
498
499void printf_unlocked(const char *format, ...) {
500 va_list args;
501 va_start(args, format);
502 vprintf(NULL, format, args);
503 va_end(args);
504}
505
506void printf(const char *format, ...) TSA_NO_ANALYSIS {
507 bool i = irq_disable_save();
508
509 bool lock = !atomic_load_rlx(&global.panicked);
510
511 if (lock)
512 raw_spin_lock(lock: &k_printf_lock.raw);
513
514 va_list args;
515 va_start(args, format);
516 vprintf(NULL, format, args);
517 va_end(args);
518
519 if (lock)
520 raw_spin_unlock(lock: &k_printf_lock.raw);
521
522 if (i)
523 irq_enable();
524}
525
526int vsnprintf(char *buffer, int buffer_len, const char *format, va_list args) {
527 if (!buffer_len)
528 buffer_len = INT_MAX;
529 struct printf_cursor csr = {
530 .buffer = buffer,
531 .buffer_len = buffer_len,
532 .cursor = 0,
533 };
534 vprintf(csr: &csr, format, args);
535 if (buffer)
536 csr.buffer[csr.cursor] = '\0';
537 return csr.cursor;
538}
539
540int snprintf(char *buffer, int buffer_len, const char *format, ...) {
541 va_list args;
542 va_start(args, format);
543 if (!buffer_len)
544 buffer_len = INT_MAX;
545 struct printf_cursor csr = {
546 .buffer = buffer,
547 .buffer_len = buffer_len,
548 .cursor = 0,
549 };
550 vprintf(csr: &csr, format, args);
551 va_end(args);
552 if (buffer)
553 csr.buffer[csr.cursor] = '\0';
554 return csr.cursor;
555}
556