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