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