diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 17:38:57 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 17:38:57 +0200 |
| commit | 8f05ea44f69cb796e7b1f581ba1b8045c98d615b (patch) | |
| tree | 3049f0a5fc33dd08f94af0e6493c588b2a4818b9 /rsnix/src/serial.rs | |
| parent | CONTEXT.md: versioning policy (a.b.c — overhaul/feature/fix) (diff) | |
| download | rs-dos-8f05ea44f69cb796e7b1f581ba1b8045c98d615b.tar.gz rs-dos-8f05ea44f69cb796e7b1f581ba1b8045c98d615b.zip | |
rsnix: preemptive kernel tasks (ADR 0003) — sched, IST fault stacks, 176-byte frames
- sched: 16-slot static table, shell=task 0, idle=task 1, spawn with
guard page + 8 KiB stack, RR on the 100 Hz tick, int 0x60 yield,
sleep_ms, lazy reap, jobs/bg/sleep/kill shell commands
- gdt: own GDT mirroring UEFI selectors + TSS with IST1/IST2
(guard-page overflows die gracefully instead of triple-faulting)
- interrupts: isr_dispatch returns next frame in rax; IntFrame grows
rsp/ss fields — QEMU's 64-bit iretq always pops all five slots from
a 176-byte frame, so synthetic task frames must carry rsp=top+ss
(a 160-byte frame made the first iretq pop rsp/ss from the unmapped
page beyond the frame)
- panic handler: task panics abort the task, shell keeps running
- main: sched::init moved after page allocator + adopted tables
(the idle's stack+guard need both; it was silently not spawning)
Diffstat (limited to '')
| -rw-r--r-- | rsnix/src/serial.rs | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/rsnix/src/serial.rs b/rsnix/src/serial.rs index 484a114..2267225 100644 --- a/rsnix/src/serial.rs +++ b/rsnix/src/serial.rs | |||
| @@ -60,8 +60,17 @@ pub fn try_get_byte() -> Option<u8> { | |||
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | /// Write a string to serial. | 63 | /// Write a string to serial, holding the scheduler lock for the whole |
| 64 | /// write so a line can't be interleaved with another task's output. | ||
| 64 | pub fn write_str(s: &str) { | 65 | pub fn write_str(s: &str) { |
| 66 | crate::sched::lock(); | ||
| 67 | write_raw(s); | ||
| 68 | crate::sched::unlock(); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Raw serial write — no scheduler lock. Callers must hold the lock | ||
| 72 | /// (klog::emit does) or be in a context where preemption is impossible. | ||
| 73 | pub fn write_raw(s: &str) { | ||
| 65 | for &b in s.as_bytes() { | 74 | for &b in s.as_bytes() { |
| 66 | if b == b'\n' { | 75 | if b == b'\n' { |
| 67 | put_byte(b'\r'); | 76 | put_byte(b'\r'); |
| @@ -70,6 +79,16 @@ pub fn write_str(s: &str) { | |||
| 70 | } | 79 | } |
| 71 | } | 80 | } |
| 72 | 81 | ||
| 82 | /// One scheduler-locked call: the line plus its CRLF, atomic with | ||
| 83 | /// respect to context switches (used by the shell's term_println, where | ||
| 84 | /// the line and its newline must not be split by a tick). | ||
| 85 | pub fn write_line(s: &str) { | ||
| 86 | crate::sched::lock(); | ||
| 87 | write_raw(s); | ||
| 88 | write_raw("\r\n"); | ||
| 89 | crate::sched::unlock(); | ||
| 90 | } | ||
| 91 | |||
| 73 | // --------------------------------------------------------------------------- | 92 | // --------------------------------------------------------------------------- |
| 74 | // fmt::Write implementation | 93 | // fmt::Write implementation |
| 75 | // --------------------------------------------------------------------------- | 94 | // --------------------------------------------------------------------------- |
| @@ -96,17 +115,17 @@ macro_rules! serial_print { | |||
| 96 | /// here, so the ring captures the whole boot sequence for free. | 115 | /// here, so the ring captures the whole boot sequence for free. |
| 97 | /// User-facing shell output must NOT use this macro (shell output would | 116 | /// User-facing shell output must NOT use this macro (shell output would |
| 98 | /// pollute the boot log); it writes to the serial port directly. | 117 | /// pollute the boot log); it writes to the serial port directly. |
| 118 | /// | ||
| 119 | /// The whole line is formatted, written, and logged under one scheduler | ||
| 120 | /// lock (klog::emit), so lines from different tasks never interleave. | ||
| 99 | #[macro_export] | 121 | #[macro_export] |
| 100 | macro_rules! serial_println { | 122 | macro_rules! serial_println { |
| 101 | () => { | 123 | () => { |
| 102 | $crate::serial::write_str("\r\n"); | 124 | $crate::klog::emit(core::format_args!("")) |
| 103 | $crate::klog::append_fmt(core::format_args!("")); | 125 | }; |
| 126 | ($($arg:tt)*) => { | ||
| 127 | $crate::klog::emit(core::format_args!($($arg)*)) | ||
| 104 | }; | 128 | }; |
| 105 | ($($arg:tt)*) => {{ | ||
| 106 | $crate::serial_print!($($arg)*); | ||
| 107 | $crate::serial::write_str("\r\n"); | ||
| 108 | $crate::klog::append_fmt(core::format_args!($($arg)*)); | ||
| 109 | }}; | ||
| 110 | } | 129 | } |
| 111 | 130 | ||
| 112 | // --------------------------------------------------------------------------- | 131 | // --------------------------------------------------------------------------- |
