Voxia OS v0.0.1
Hobby Project Operating System Targeting x86-64
Loading...
Searching...
No Matches
serial.c
Go to the documentation of this file.
1#include "serial.h"
2#include <spinlock.h>
3#include <type.h>
4#include <init/init.h>
5#include <libk/io.h>
6#include <str.h>
7
8#define INT_ENABLE_OFFSET 1
9#define SERIAL_BUFFER2_SIZE (4096 * 3)
10#define SERIAL_BUFFER2_MASK (SERIAL_BUFFER2_SIZE - 1)
11
12/* Nilai sentinel: slot sedang ditulis oleh producer */
13#define SLOT_WRITING 0xFE
14/* Nilai sentinel: slot sengaja dibuang (drop) */
15#define SLOT_DROPPED 0xFF
16/* Nilai sentinel: slot kosong / sudah dikonsumsi */
17#define SLOT_EMPTY 0x00
18
19/* Batas spin sebelum menyerah (cepat agar tidak hang) */
20#define SPIN_LIMIT 10000u
21
22typedef struct {
23 char data[128];
24 uint8_t len; /* SLOT_EMPTY / SLOT_WRITING / SLOT_DROPPED / 1-128 */
26
27typedef struct {
29 uint32_t head; /* next slot yang akan di-claim producer */
30 uint32_t tail; /* next slot yang akan dikonsumsi consumer */
32
34
35/*
36 * FIX: flush_owner memastikan hanya SATU core yang flush pada satu waktu.
37 * Menggunakan __atomic_test_and_set / __atomic_clear (spinlock 1-bit).
38 */
39static volatile unsigned char __flush_lock = 0;
41
42/* ------------------------------------------------------------------ */
43/* Hardware setup */
44/* ------------------------------------------------------------------ */
45
46void serial_setup(void) {
48 outb(SERIAL_COM1 + 3, 0x80);
49 outb(SERIAL_COM1 + 0, 0x03);
51 outb(SERIAL_COM1 + 3, 0x03);
52 outb(SERIAL_COM1 + 2, 0xC7);
53 outb(SERIAL_COM1 + 4, 0x0B);
54}
55
57 return inb(SERIAL_COM1 + 5) & 0x20;
58}
59
60extern void serial_putc(char c) {
61 while (serial_is_transmit_empty() == 0)
62 __asm__ volatile("pause");
63
65}
66
68 for (int i = 0; str[i] != '\0'; i++)
69 serial_putc(str[i]);
70}
71
73 if (base < 2 || base > 16)
74 return;
75
76 const char* digits = "0123456789ABCDEF";
77 char buf[65];
78 int i = 0;
79 bool negative = false;
80
81 uint64_t n;
82 if (base == 10 && num < 0) {
83 negative = true;
84 n = (uint64_t) (-num);
85 } else {
86 n = (uint64_t) num;
87 }
88
89 if (n == 0) {
90 buf[i++] = '0';
91 } else {
92 while (n > 0) {
93 buf[i++] = digits[n % (uint64_t) base];
94 n /= (uint64_t) base;
95 }
96 }
97
98 if (negative)
99 buf[i++] = '-';
100
101 for (int j = i - 1; j >= 0; j--)
102 serial_putc(buf[j]);
103}
104
105void serial_clear(void) {
106 serial_printf("\033[2J\033[H");
107}
108
109/* ------------------------------------------------------------------ */
110/* Ring-buffer internals */
111/* ------------------------------------------------------------------ */
112
113/*
114 * reserve_slot: klaim satu slot di ring-buffer secara atomik.
115 *
116 * FIX 1: tambah __asm__ volatile("pause") di CAS loop agar tidak
117 * thrash pipeline CPU pada 8+ core.
118 * FIX 2: perbandingan (head - tail) tetap aman karena unsigned wraparound,
119 * tapi sekarang kita juga re-load tail di setiap iterasi agar
120 * tidak stuck ketika consumer maju.
121 */
122static bool reserve_slot(uint32_t* out_idx) {
124
125 for (;;) {
126 head = __atomic_load_n(&__buffer.head, __ATOMIC_RELAXED);
127 tail = __atomic_load_n(&__buffer.tail, __ATOMIC_ACQUIRE);
128
129 if ((head - tail) >= SERIAL_BUFFER2_SIZE)
130 return false; /* buffer penuh, buang pesan */
131
132 if (__atomic_compare_exchange_n(&__buffer.head, &head, head + 1,
133 true, __ATOMIC_ACQ_REL,
134 __ATOMIC_RELAXED))
135 break;
136
137 /* FIX: hint ke CPU agar pipeline tidak thrash */
138 __asm__ volatile("pause");
139 }
140
141 *out_idx = head & SERIAL_BUFFER2_MASK;
142 return true;
143}
144
145/*
146 * put_into_buffer: tulis string ke slot yang sudah di-claim.
147 *
148 * FIX 3: ganti infinite busy-wait dengan spin ber-batas + SLOT_DROPPED.
149 * Kalau setelah SPIN_LIMIT iterasi slot masih belum bebas
150 * (artinya consumer sangat lambat atau macet), kita tandai slot
151 * sebagai DROPPED supaya consumer bisa skip dan tidak hang.
152 *
153 * FIX 4: tandai SLOT_WRITING segera setelah claim sehingga consumer tahu
154 * slot ini sedang diisi dan jangan di-skip sebelum waktunya.
155 */
156static void put_into_buffer(const char* str, uint8_t len) {
158 if (!reserve_slot(&idx))
159 return;
160
161 serial_entry_t* entry = &__buffer.buffer[idx];
162
163 /*
164 * Tunggu sampai consumer selesai mengosongkan slot ini.
165 * Slot bisa masih terisi kalau ring buffer wrap-around dan
166 * consumer belum sampai ke sini.
167 */
168 uint32_t spin = 0;
169 while (__atomic_load_n(&entry->len, __ATOMIC_ACQUIRE) != SLOT_EMPTY) {
170 if (++spin >= SPIN_LIMIT) {
171 /*
172 * Consumer macet atau sangat lambat.
173 * Tandai slot sebagai DROPPED agar head/tail tetap
174 * konsisten dan core lain tidak ikut macet.
175 */
176 __atomic_store_n(&entry->len, SLOT_DROPPED,
177 __ATOMIC_RELEASE);
178 return;
179 }
180 __asm__ volatile("pause");
181 }
182
183 /* Tandai "sedang ditulis" — consumer akan skip sampai len berubah */
184 __atomic_store_n(&entry->len, SLOT_WRITING, __ATOMIC_RELEASE);
185
186 /* Salin data */
187 if (len > (uint8_t) sizeof(entry->data))
188 len = (uint8_t) sizeof(entry->data);
189
190 for (uint8_t i = 0; i < len; i++)
191 entry->data[i] = str[i];
192
193 /* Publish ke consumer */
194 __atomic_store_n(&entry->len, len, __ATOMIC_RELEASE);
195}
196
197/* ------------------------------------------------------------------ */
198/* Buffered number/string helpers (multicore path) */
199/* ------------------------------------------------------------------ */
200
201static void serial2_send_number(int64_t num, int base) {
202 const char* digits = "0123456789ABCDEF";
203 char buf[65];
204 int i = 0;
205 bool negative = false;
206
207 if (base == 10 && num < 0) {
208 negative = true;
209 num = -num;
210 }
211
212 uint64_t n = (uint64_t) num;
213
214 if (n == 0) {
215 buf[i++] = '0';
216 } else {
217 while (n > 0 && i < 64) {
218 buf[i++] = digits[n % (uint64_t) base];
219 n /= (uint64_t) base;
220 }
221 }
222
223 if (negative && i < 64)
224 buf[i++] = '-';
225
226 /* balik urutan */
227 char buf2[65];
228 __builtin_memset(buf2, 0, sizeof(buf2));
229 for (int j = i - 1; j >= 0; j--)
230 buf2[i - j - 1] = buf[j];
231
232 put_into_buffer(buf2, (uint8_t) i);
233}
234
235static void serial2_send_unsigned_number(uint64_t num, int base, int limit) {
236 const char* digits = "0123456789ABCDEF";
237 char buf[65];
238 int i = 0;
239
240 if (base < 2 || base > 16) {
241 put_into_buffer("?", 1);
242 return;
243 }
244
245 uint64_t n = num;
246
247 if (n == 0) {
248 buf[i++] = '0';
249 } else {
250 while (n > 0 && i < 64) {
251 buf[i++] = digits[n % (uint64_t) base];
252 n /= (uint64_t) base;
253 }
254 }
255
256 int count = (limit > 0 && limit < i) ? limit : i;
257 int start = i - count;
258 int end = i - 1;
259
260 char buf2[65] = {0};
261 for (int j = end; j >= start; j--)
262 buf2[end - j] = buf[j];
263
265}
266
267/* ------------------------------------------------------------------ */
268/* Format parsers */
269/* ------------------------------------------------------------------ */
270
271void parse_multicore(__builtin_va_list args, const char* fmt) {
272 char temp_buffer[128];
273 uint8_t temp_index = 0;
274
275 for (const char* p = fmt; *p != '\0'; p++) {
276 if (*p != '%') {
277 temp_buffer[temp_index++] = *p;
278 if (temp_index == 128) {
279 put_into_buffer(temp_buffer, temp_index);
280 temp_index = 0;
281 }
282 continue;
283 }
284
285 if (temp_index > 0) {
286 put_into_buffer(temp_buffer, temp_index);
287 temp_index = 0;
288 }
289
290 p++;
291 if (*p == 'd') {
292 int i = __builtin_va_arg(args, int);
294 } else if (*p == 'l') {
295 p++;
296 if (*p == 'u') {
297 uint64_t i = __builtin_va_arg(args, uint64_t);
299 } else if (*p == 'd') {
300 int64_t i = __builtin_va_arg(args, int64_t);
301 serial2_send_number(i, 10);
302 }
303 } else if (*p == 'p') {
304 uintptr_t ptr = __builtin_va_arg(args, uintptr_t);
306 } else if (*p == 'x') {
307 uint64_t i = __builtin_va_arg(args, uint64_t);
309 } else if (*p == 's') {
310 char* s = __builtin_va_arg(args, char*);
311 if (!s) {
312 put_into_buffer("(null)", 6);
313 } else {
314 size_t slen = strlen(s);
315 uint8_t blen =
316 (slen > 128) ? 128 : (uint8_t) slen;
317 put_into_buffer(s, blen);
318 }
319 } else if (*p == 'b') {
320 uint64_t i = __builtin_va_arg(args, uint64_t);
322 } else if (*p == 'B') {
323 int i = __builtin_va_arg(args, int);
324 if ((boolean_t) i)
325 put_into_buffer("True", 4);
326 else
327 put_into_buffer("False", 5);
328 } else if (*p == '.') {
329 p++;
330 int precision = 0;
331 while (*p >= '0' && *p <= '9') {
332 precision = precision * 10 + (*p - '0');
333 p++;
334 }
335 if (*p == 'x') {
336 uint64_t i = __builtin_va_arg(args, uint64_t);
337 serial2_send_unsigned_number(i, 16, precision);
338 }
339 }
340 }
341
342 if (temp_index > 0)
343 put_into_buffer(temp_buffer, temp_index);
344}
345
346/* ------------------------------------------------------------------ */
347/* Flush — hanya boleh efektif dari 1 core pada satu waktu */
348/* ------------------------------------------------------------------ */
349
350/*
351 * FIX 5: __flush_lock memastikan hanya satu core yang mengonsumsi
352 * tail pada satu waktu. Core lain yang memanggil flush
353 * langsung return — mereka tidak perlu flush karena core
354 * pemenang akan menguras semua entry yang sudah siap.
355 *
356 * FIX 6: consumer sekarang handle SLOT_WRITING dengan spin ber-batas
357 * (bukan infinite) dan handle SLOT_DROPPED dengan skip.
358 */
359void serial2_flush(void) {
360 /* Coba ambil lock; kalau gagal, core lain sedang flush — tidak apa. */
361 if (__atomic_test_and_set(&__flush_lock, __ATOMIC_ACQUIRE))
362 return;
363
364 for (;;) {
365 uint32_t tail =
366 __atomic_load_n(&__buffer.tail, __ATOMIC_RELAXED);
367 uint32_t head =
368 __atomic_load_n(&__buffer.head, __ATOMIC_ACQUIRE);
369
370 if (tail == head)
371 break; /* buffer kosong */
372
373 serial_entry_t* entry =
375
376 /* Tunggu producer selesai menulis, tapi dengan batas spin */
377 uint32_t spin = 0;
378 uint8_t len;
379 for (;;) {
380 len = __atomic_load_n(&entry->len, __ATOMIC_ACQUIRE);
381 if (len != SLOT_EMPTY && len != SLOT_WRITING)
382 break; /* DROPPED atau data valid */
383 if (len == SLOT_EMPTY)
384 break; /* belum ada produsen sama sekali — unusual */
385 if (++spin >= SPIN_LIMIT)
386 break; /* producer macet, skip saja */
387 __asm__ volatile("pause");
388 }
389
390 /* Kirim ke hardware kalau bukan sentinel */
391 if (len != SLOT_EMPTY && len != SLOT_WRITING
392 && len != SLOT_DROPPED) {
393 for (uint8_t i = 0; i < len; i++)
394 serial_putc(entry->data[i]);
395 }
396
397 /* Bebaskan slot */
398 __atomic_store_n(&entry->len, SLOT_EMPTY, __ATOMIC_RELEASE);
399
400 /* Maju tail — hanya kita yang pegang lock, jadi tidak perlu CAS */
401 __atomic_store_n(&__buffer.tail, tail + 1, __ATOMIC_RELEASE);
402 }
403
404 __atomic_clear(&__flush_lock, __ATOMIC_RELEASE);
405}
406
407/* ------------------------------------------------------------------ */
408/* Direct (pre-multicore) path */
409/* ------------------------------------------------------------------ */
410
411static void serial_send_padded(uint64_t val, int base, int width, char pad) {
412 const char* digits = "0123456789ABCDEF";
413 char buf[65];
414 int i = 0;
415
416 if (val == 0) {
417 buf[i++] = '0';
418 } else {
419 uint64_t n = val;
420 while (n > 0) {
421 buf[i++] = digits[n % (uint64_t) base];
422 n /= (uint64_t) base;
423 }
424 }
425
426 for (int p = i; p < width; p++)
427 serial_putc(pad);
428
429 for (int j = i - 1; j >= 0; j--)
430 serial_putc(buf[j]);
431}
432
433static void parse_before_multicore(__builtin_va_list args, const char* fmt) {
434 for (const char* p = fmt; *p != '\0'; p++) {
435 if (*p != '%') {
436 serial_putc(*p);
437 continue;
438 }
439 p++;
440
441 char pad = ' ';
442 if (*p == '0') {
443 pad = '0';
444 p++;
445 }
446
447 int width = 0;
448 while (*p >= '0' && *p <= '9') {
449 width = width * 10 + (*p - '0');
450 p++;
451 }
452
453 int precision = 4;
454 if (*p == '.') {
455 p++;
456 precision = 0;
457 while (*p >= '0' && *p <= '9') {
458 precision = precision * 10 + (*p - '0');
459 p++;
460 }
461 }
462 (void) precision;
463
464 bool is_long = false;
465 if (*p == 'l') {
466 is_long = true;
467 p++;
468 }
469
470 switch (*p) {
471 case 'd': {
472 int64_t i =
473 is_long ? __builtin_va_arg(args, int64_t)
474 : (int64_t) __builtin_va_arg(args, int);
475 if (i < 0) {
476 serial_putc('-');
477 serial_send_padded((uint64_t) (-i), 10,
478 width ? width - 1 : 0, pad);
479 } else {
481 pad);
482 }
483 break;
484 }
485 case 'u': {
486 uint64_t i = is_long ? __builtin_va_arg(args, uint64_t)
487 : (uint64_t) __builtin_va_arg(
488 args, unsigned int);
489 serial_send_padded(i, 10, width, pad);
490 break;
491 }
492 case 'x': {
493 uint64_t i = is_long ? __builtin_va_arg(args, uint64_t)
494 : (uint64_t) __builtin_va_arg(
495 args, unsigned int);
496 serial_send_padded(i, 16, width, pad);
497 break;
498 }
499 case 'b': {
500 uint64_t i = is_long ? __builtin_va_arg(args, uint64_t)
501 : (uint64_t) __builtin_va_arg(
502 args, unsigned int);
503 serial_send_padded(i, 2, width, pad);
504 break;
505 }
506 case 's': {
507 char* s = __builtin_va_arg(args, char*);
508 if (!s)
509 s = "(null)";
511 break;
512 }
513 case 'B': {
514 int i = __builtin_va_arg(args, int);
515 serial_send_string(i ? "true" : "false");
516 break;
517 }
518 case 'c': {
519 char c = (char) __builtin_va_arg(args, int);
520 serial_putc(c);
521 break;
522 }
523 case '%':
524 serial_putc('%');
525 break;
526 default:
527 serial_putc('%');
528 serial_putc(*p);
529 break;
530 }
531 }
532}
533
534/* ------------------------------------------------------------------ */
535/* Public API */
536/* ------------------------------------------------------------------ */
537
539
540#include <hal/cpu/irq_lock.h>
541
542KERNEL_API void serial2_printf(const char* fmt, ...) {
545
546 __builtin_va_list args;
547 __builtin_va_start(args, fmt);
548 if (!multicore_start)
549 parse_before_multicore(args, fmt);
550 else
551 parse_multicore(args, fmt);
552 __builtin_va_end(args);
553
556}
557
558KERNEL_API void serial_printf(const char* fmt, ...) {
561
562 __builtin_va_list args;
563 __builtin_va_start(args, fmt);
564 parse_before_multicore(args, fmt);
565 __builtin_va_end(args);
566
569}
int count
Definition cache.h:2
#define SLOT_WRITING
Definition console.c:10
#define SPIN_LIMIT
Definition console.c:15
#define SLOT_EMPTY
Definition console.c:9
#define SLOT_DROPPED
Definition console.c:11
boolean_t multicore_start
Definition core.c:105
elf_section_map uintptr_t base
Definition elf.h:296
uint16_t flags
Definition thread.h:5
static uint8_t inb(uint16_t port)
Definition io.h:11
static void outb(uint16_t port, uint8_t value)
Definition io.h:6
void serial_printf(const char *fmt,...)
void serial2_printf(const char *fmt,...)
static void irq_restore(uintptr_t flags)
Definition irq_lock.h:20
static uintptr_t irq_save(void)
Definition irq_lock.h:6
size_t len
Definition oct2bin.h:7
static void serial2_send_unsigned_number(uint64_t num, int base, int limit)
Definition serial.c:235
int serial_is_transmit_empty(void)
Definition serial.c:56
static void parse_before_multicore(__builtin_va_list args, const char *fmt)
Definition serial.c:433
#define SERIAL_BUFFER2_SIZE
Definition serial.c:9
static spinlock_t __serial2_producer_lock
Definition serial.c:40
static serial_ring_buffer_t __buffer
Definition serial.c:33
void serial2_flush(void)
Definition serial.c:359
void parse_multicore(__builtin_va_list args, const char *fmt)
Definition serial.c:271
void serial_clear(void)
Definition serial.c:105
void serial_putc(char c)
Definition serial.c:60
#define INT_ENABLE_OFFSET
Definition serial.c:8
static volatile unsigned char __flush_lock
Definition serial.c:39
static bool reserve_slot(uint32_t *out_idx)
Definition serial.c:122
static void serial_send_padded(uint64_t val, int base, int width, char pad)
Definition serial.c:411
void serial_send_string(char *str)
Definition serial.c:67
static void put_into_buffer(const char *str, uint8_t len)
Definition serial.c:156
void serial_send_number(int64_t num, int base)
Definition serial.c:72
void serial_setup(void)
Definition serial.c:46
#define SERIAL_BUFFER2_MASK
Definition serial.c:10
static void serial2_send_number(int64_t num, int base)
Definition serial.c:201
#define SERIAL_COM1
Definition serial.h:6
void spin_acquire(spinlock_t *lock)
Definition spinlock.c:8
void spin_release(spinlock_t *lock)
Definition spinlock.c:19
size_t strlen(const char *s)
Definition str.c:105
kstring str(const char *str)
Definition serial.c:22
uint8_t len
Definition serial.c:24
char data[128]
Definition serial.c:23
serial_entry_t buffer[(4096 *3)]
Definition serial.c:28
uint32_t tail
Definition tty.h:11
uint32_t head
Definition tty.h:10
unsigned int uint32_t
Definition type.h:19
#define KERNEL_API
Definition type.h:93
uint8_t boolean_t
Definition type.h:89
unsigned long uintptr_t
Definition type.h:73
unsigned long uint64_t
Definition type.h:25
unsigned char uint8_t
Definition type.h:7
signed long int64_t
Definition type.h:49
uint32_t width
Definition virtio-gpu.hpp:2
uint16_t idx
Definition virtio-gpu.hpp:1
uint64_t ptr
Definition xhci.hpp:0