Skip to content

Commit f7f01bd

Browse files
committed
add async interrupt safe synchronization primitives
An asynchronous interface has been added to the interrupt-safe mutex so that interrupt handlers and asynchronous tasks can be synchronized. The mutex was demonstrated by using the shell as an example, where the asynchronous task communicates with the serial interface and its interrupt handler.
1 parent 7614614 commit f7f01bd

File tree

9 files changed

+389
-14
lines changed

9 files changed

+389
-14
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ vga = []
6868
common-os = []
6969
nostd = []
7070
semihosting = ["dep:semihosting"]
71-
shell = ["simple-shell"]
71+
shell = []
7272

7373
[dependencies]
7474
hermit-macro = { path = "hermit-macro" }
@@ -106,6 +106,7 @@ talc = { version = "4" }
106106
time = { version = "0.3", default-features = false }
107107
volatile = { version = "0.5.4", features = ["unstable"] }
108108
zerocopy = { version = "0.7", features = ["derive"] }
109+
interrupts = "0.1"
109110

110111
[dependencies.smoltcp]
111112
version = "0.11"

src/arch/x86_64/kernel/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ use core::ptr;
55
use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
66

77
use hermit_entry::boot_info::{BootInfo, PlatformInfo, RawBootInfo};
8-
use hermit_sync::InterruptSpinMutex;
98
use x86::controlregs::{cr0, cr0_write, cr4, Cr0};
109

1110
use self::serial::SerialPort;
1211
use crate::arch::mm::{PhysAddr, VirtAddr};
1312
use crate::arch::x86_64::kernel::core_local::*;
1413
use crate::env::{self, is_uhyve};
14+
use crate::synch::r#async::AsyncInterruptMutex;
1515

1616
#[cfg(feature = "acpi")]
1717
pub mod acpi;
@@ -52,7 +52,7 @@ pub fn raw_boot_info() -> &'static RawBootInfo {
5252
}
5353

5454
/// Serial port to print kernel messages
55-
pub(crate) static COM1: InterruptSpinMutex<Option<SerialPort>> = InterruptSpinMutex::new(None);
55+
pub(crate) static COM1: AsyncInterruptMutex<Option<SerialPort>> = AsyncInterruptMutex::new(None);
5656

5757
pub fn get_ram_address() -> PhysAddr {
5858
PhysAddr(boot_info().hardware_info.phys_addr_range.start)

src/shell/constants.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pub(crate) const CTRL_L: u8 = 12;
2+
pub(crate) const ENTER: u8 = 13;
3+
pub(crate) const BACKSPACE: u8 = 127;
4+
pub(crate) const CTRL_C: u8 = 3;
5+
pub(crate) const ESCAPE: u8 = 27;
6+
7+
pub(crate) const CSI: u8 = 91;
8+
pub(crate) const CSI_UP: u8 = 65;
9+
pub(crate) const CSI_DOWN: u8 = 66;
10+
pub(crate) const CSI_RIGHT: u8 = 67;
11+
pub(crate) const CSI_LEFT: u8 = 68;

src/shell.rs renamed to src/shell/mod.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
/// This shell implementation is derived form
2+
/// https://github.com/explodingcamera/pogos/tree/main/crates/simple-shell
13
use hermit_sync::Lazy;
2-
use simple_shell::*;
34

4-
use crate::arch::kernel::COM1;
5+
use self::shell::*;
56
use crate::interrupts::print_statistics;
67

7-
fn read() -> Option<u8> {
8-
COM1.lock().as_mut().map(|s| s.read())?
9-
}
8+
mod constants;
9+
mod shell;
10+
mod writer;
1011

1112
static mut SHELL: Lazy<Shell<'_>> = Lazy::new(|| {
12-
let (print, read) = (|s: &str| print!("{}", s), read);
13-
let mut shell = Shell::new(print, read);
13+
let print = |s: &str| print!("{}", s);
14+
let mut shell = Shell::new(print);
1415

1516
shell.commands.insert(
1617
"help",
@@ -27,7 +28,7 @@ static mut SHELL: Lazy<Shell<'_>> = Lazy::new(|| {
2728
"interrupts",
2829
ShellCommand {
2930
help: "Shows the number of received interrupts",
30-
func: |_, shell| {
31+
func: |_, _shell| {
3132
print_statistics();
3233
Ok(())
3334
},
@@ -38,9 +39,8 @@ static mut SHELL: Lazy<Shell<'_>> = Lazy::new(|| {
3839
"shutdown",
3940
ShellCommand {
4041
help: "Shutdown HermitOS",
41-
func: |_, shell| {
42+
func: |_, _shell| {
4243
crate::shutdown(0);
43-
Ok(())
4444
},
4545
aliases: &["s"],
4646
},
@@ -50,6 +50,5 @@ static mut SHELL: Lazy<Shell<'_>> = Lazy::new(|| {
5050
});
5151

5252
pub(crate) fn init() {
53-
// Also supports async
5453
crate::executor::spawn(unsafe { SHELL.run_async() });
5554
}

src/shell/shell.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use alloc::collections::BTreeMap;
2+
use alloc::string::{String, ToString};
3+
use alloc::vec::Vec;
4+
use core::future::{self, Future};
5+
use core::task::{ready, Poll};
6+
7+
use super::constants::*;
8+
use super::writer::*;
9+
use crate::arch::kernel::COM1;
10+
11+
#[derive(Clone, Copy)]
12+
pub struct ShellCommand<'a> {
13+
pub help: &'a str,
14+
pub func: fn(&[&str], &mut Shell<'a>) -> Result<(), &'a str>,
15+
pub aliases: &'a [&'a str],
16+
}
17+
18+
pub struct Shell<'a> {
19+
pub history: Vec<String>,
20+
pub commands: BTreeMap<&'a str, ShellCommand<'a>>,
21+
pub command: String,
22+
pub cursor: usize,
23+
writer: Writer,
24+
}
25+
26+
impl<'a> Shell<'a> {
27+
pub fn new(write: fn(&str)) -> Self {
28+
Self {
29+
history: Vec::new(),
30+
commands: BTreeMap::new(),
31+
command: String::new(),
32+
cursor: 0,
33+
writer: Writer::new(write),
34+
}
35+
}
36+
37+
async fn get_char_async(&self) -> u8 {
38+
future::poll_fn(|cx| {
39+
let mut pinned = core::pin::pin!(COM1.async_lock());
40+
let mut guard = ready!(pinned.as_mut().poll(cx));
41+
if let Some(Some(c)) = guard.as_mut().map(|s| s.read()) {
42+
Poll::Ready(c)
43+
} else {
44+
cx.waker().wake_by_ref();
45+
Poll::Pending
46+
}
47+
})
48+
.await
49+
}
50+
51+
#[allow(dead_code)]
52+
pub fn with_commands(mut self, mut commands: BTreeMap<&'a str, ShellCommand<'a>>) -> Self {
53+
self.commands.append(&mut commands);
54+
self
55+
}
56+
57+
pub async fn run_async(&mut self) {
58+
self.print_prompt();
59+
60+
loop {
61+
let c = self.get_char_async().await;
62+
match c {
63+
ESCAPE => self.handle_escape_async().await,
64+
_ => self.match_char(c),
65+
}
66+
}
67+
}
68+
69+
fn match_char(&mut self, b: u8) {
70+
match b {
71+
CTRL_C => self.process_command("exit".to_string()),
72+
CTRL_L => self.handle_clear(),
73+
ENTER => self.handle_enter(),
74+
BACKSPACE => self.handle_backspace(),
75+
c if (32..=126).contains(&c) => {
76+
self.command.insert(self.cursor, c as char);
77+
self.cursor += 1;
78+
79+
if self.cursor < self.command.len() {
80+
// Print the remaining text
81+
shell_print!(self.writer, "{}", &self.command[self.cursor - 1..]);
82+
// Move cursor to the correct position
83+
shell_print!(self.writer, "\x1b[{}D", self.command.len() - self.cursor);
84+
} else {
85+
shell_print!(self.writer, "{}", c as char);
86+
}
87+
}
88+
_ => {}
89+
}
90+
}
91+
92+
fn handle_clear(&mut self) {
93+
self.clear_screen();
94+
self.print_prompt();
95+
shell_print!(self.writer, "{}", self.command);
96+
self.cursor = self.command.len();
97+
}
98+
99+
fn handle_backspace(&mut self) {
100+
if self.cursor > 0 {
101+
self.command.remove(self.cursor - 1);
102+
self.cursor -= 1;
103+
shell_print!(self.writer, "\x08"); // Move cursor left
104+
shell_print!(self.writer, "{}", &self.command[self.cursor..]); // Print the remaining text
105+
shell_print!(self.writer, " "); // Clear last character
106+
shell_print!(
107+
self.writer,
108+
"\x1b[{}D",
109+
self.command.len() - self.cursor + 1
110+
);
111+
// Move cursor to the correct position
112+
}
113+
}
114+
115+
fn handle_enter(&mut self) {
116+
shell_println!(self.writer, "");
117+
self.process_command(self.command.clone());
118+
self.history.push(self.command.clone());
119+
self.command.clear();
120+
self.cursor = 0;
121+
self.print_prompt();
122+
}
123+
124+
async fn handle_escape_async(&mut self) {
125+
if self.get_char_async().await != CSI {
126+
return;
127+
}
128+
let b = self.get_char_async().await;
129+
self._handle_escape(b);
130+
}
131+
132+
fn _handle_escape(&mut self, b: u8) {
133+
match b {
134+
CSI_UP => {}
135+
CSI_DOWN => {}
136+
CSI_RIGHT => {
137+
if self.cursor < self.command.len() {
138+
shell_print!(self.writer, "\x1b[1C");
139+
self.cursor += 1;
140+
}
141+
}
142+
CSI_LEFT => {
143+
if self.cursor > 0 {
144+
shell_print!(self.writer, "\x1b[1D");
145+
self.cursor -= 1;
146+
}
147+
}
148+
_ => {}
149+
}
150+
}
151+
152+
fn process_command(&mut self, command: String) {
153+
let mut args = command.split_whitespace();
154+
let command = args.next().unwrap_or("");
155+
let args = args.collect::<Vec<_>>();
156+
157+
for (name, shell_command) in &self.commands {
158+
if shell_command.aliases.contains(&command) || name == &command {
159+
return (shell_command.func)(&args, self).unwrap_or_else(|err| {
160+
shell_println!(self.writer, "{}: {}", command, err);
161+
});
162+
}
163+
}
164+
165+
if command.is_empty() {
166+
return;
167+
}
168+
169+
shell_println!(self.writer, "{}: command not found", command);
170+
}
171+
172+
pub fn print_help_screen(&mut self) {
173+
shell_println!(self.writer, "available commands:");
174+
for (name, command) in &self.commands {
175+
shell_print!(self.writer, " {:<12}{:<25}", name, command.help);
176+
if !command.aliases.is_empty() {
177+
shell_print!(self.writer, " aliases: {}", command.aliases.join(", "));
178+
}
179+
shell_println!(self.writer, "");
180+
}
181+
}
182+
183+
pub fn print_prompt(&mut self) {
184+
shell_print!(self.writer, "> ");
185+
}
186+
187+
pub fn clear_screen(&mut self) {
188+
shell_print!(self.writer, "\x1b[2J\x1b[1;1H");
189+
}
190+
}

src/shell/writer.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use core::fmt::Write;
2+
3+
pub(crate) struct Writer {
4+
print: fn(&str),
5+
}
6+
7+
impl core::fmt::Write for Writer {
8+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
9+
(self.print)(s);
10+
Ok(())
11+
}
12+
}
13+
14+
impl Writer {
15+
pub fn new(print: fn(&str)) -> Self {
16+
Self { print }
17+
}
18+
19+
pub fn print(&mut self, t: &str) {
20+
self.write_str(t).unwrap();
21+
}
22+
23+
pub fn print_args(&mut self, t: core::fmt::Arguments<'_>) {
24+
self.write_fmt(t).unwrap();
25+
}
26+
}
27+
28+
macro_rules! shell_print {
29+
($writer:expr, $fmt:literal$(, $($arg: tt)+)?) => {
30+
$writer.print_args(format_args!($fmt $(,$($arg)+)?))
31+
}
32+
}
33+
34+
macro_rules! shell_println {
35+
($writer:expr, $fmt:literal$(, $($arg: tt)+)?) => {{
36+
shell_print!($writer, $fmt $(,$($arg)+)?);
37+
$writer.print("\n");
38+
}};
39+
() => {
40+
$writer.print("\n");
41+
}
42+
}
43+
44+
pub(crate) use {shell_print, shell_println};

0 commit comments

Comments
 (0)