Skip to content

Commit

Permalink
Merge pull request #1096 from stlankes/shell
Browse files Browse the repository at this point in the history
add option to start a simple shell as async task
  • Loading branch information
mkroening authored Mar 13, 2024
2 parents 0ee5a48 + a3e8d91 commit 27556ba
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 28 deletions.
15 changes: 11 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ trace = []
vga = []
common-os = []
semihosting = ["dep:semihosting"]
shell = ["simple-shell"]

[dependencies]
ahash = { version = "0.8", default-features = false }
Expand Down Expand Up @@ -96,6 +97,7 @@ zerocopy = { version = "0.7", features = ["derive"] }
build-time = "0.1.3"
async-trait = "0.1.77"
async-lock = { version = "3.3.0", default-features = false }
simple-shell = { version = "0.0.1", optional = true }

[dependencies.smoltcp]
version = "0.11"
Expand Down
5 changes: 5 additions & 0 deletions src/arch/aarch64/kernel/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ impl SerialPort {
}
}

#[allow(dead_code)]
pub fn read(&mut self) -> Option<u8> {
None
}

pub fn init(&self, _baudrate: u32) {
// We don't do anything here (yet).
}
Expand Down
6 changes: 3 additions & 3 deletions src/arch/x86_64/kernel/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,17 @@ impl IrqStatistics {
}

pub(crate) fn print_statistics() {
info!("Number of interrupts");
println!("Number of interrupts");
for (core_id, irg_statistics) in IRQ_COUNTERS.lock().iter() {
for (i, counter) in irg_statistics.counters.iter().enumerate() {
let counter = counter.load(Ordering::Relaxed);
if counter > 0 {
match get_irq_name(i.try_into().unwrap()) {
Some(name) => {
info!("[{core_id}][{name}]: {counter}");
println!("[{core_id}][{name}]: {counter}");
}
_ => {
info!("[{core_id}][{i}]: {counter}");
println!("[{core_id}][{i}]: {counter}");
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/arch/x86_64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub fn raw_boot_info() -> &'static RawBootInfo {
}

/// Serial port to print kernel messages
static COM1: InterruptSpinMutex<Option<SerialPort>> = InterruptSpinMutex::new(None);
pub(crate) static COM1: InterruptSpinMutex<Option<SerialPort>> = InterruptSpinMutex::new(None);

pub fn get_ram_address() -> PhysAddr {
PhysAddr(boot_info().hardware_info.phys_addr_range.start)
Expand Down
70 changes: 50 additions & 20 deletions src/arch/x86_64/kernel/serial.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,79 @@
#[cfg(feature = "shell")]
use alloc::collections::VecDeque;

use x86_64::instructions::port::Port;

use crate::arch::x86_64::kernel::core_local::increment_irq_counter;
use crate::arch::x86_64::kernel::interrupts::{self, IDT};
use crate::arch::x86_64::kernel::{apic, COM1};

enum Inner {
const SERIAL_IRQ: u8 = 36;

enum SerialInner {
Uart(uart_16550::SerialPort),
Uhyve(Port<u8>),
}

pub struct SerialPort(Inner);
pub struct SerialPort {
inner: SerialInner,
#[cfg(feature = "shell")]
buffer: VecDeque<u8>,
}

impl SerialPort {
pub unsafe fn new(base: u16) -> Self {
if crate::env::is_uhyve() {
let serial = Port::new(base);
Self(Inner::Uhyve(serial))
Self {
inner: SerialInner::Uhyve(serial),
#[cfg(feature = "shell")]
buffer: VecDeque::new(),
}
} else {
let mut serial = unsafe { uart_16550::SerialPort::new(base) };
serial.init();
Self(Inner::Uart(serial))
Self {
inner: SerialInner::Uart(serial),
#[cfg(feature = "shell")]
buffer: VecDeque::new(),
}
}
}

pub fn receive(&mut self) -> u8 {
if let Inner::Uart(s) = &mut self.0 {
s.receive()
} else {
0
pub fn buffer_input(&mut self) {
if let SerialInner::Uart(s) = &mut self.inner {
let c = unsafe { char::from_u32_unchecked(s.receive().into()) };
#[cfg(not(feature = "shell"))]
if !c.is_ascii_control() {
print!("{}", c);
}
#[cfg(feature = "shell")]
self.buffer.push_back(c.try_into().unwrap());
}
}

#[allow(dead_code)]
#[cfg(feature = "shell")]
pub fn read(&mut self) -> Option<u8> {
self.buffer.pop_front()
}

#[allow(dead_code)]
#[cfg(not(feature = "shell"))]
pub fn read(&mut self) -> Option<u8> {
None
}

pub fn send(&mut self, buf: &[u8]) {
match &mut self.0 {
Inner::Uhyve(s) => {
match &mut self.inner {
SerialInner::Uhyve(s) => {
for &data in buf {
unsafe {
s.write(data);
}
}
}
Inner::Uart(s) => {
SerialInner::Uart(s) => {
for &data in buf {
s.send(data);
}
Expand All @@ -49,22 +83,18 @@ impl SerialPort {
}

extern "x86-interrupt" fn serial_interrupt(_stack_frame: crate::interrupts::ExceptionStackFrame) {
let c = unsafe { char::from_u32_unchecked(COM1.lock().as_mut().unwrap().receive().into()) };
if !c.is_ascii_control() {
print!("{}", c);
}
COM1.lock().as_mut().unwrap().buffer_input();
increment_irq_counter(SERIAL_IRQ);

apic::eoi();
}

pub(crate) fn install_serial_interrupt() {
const SERIAL_IRQ: usize = 36;

unsafe {
let mut idt = IDT.lock();
idt[SERIAL_IRQ]
idt[SERIAL_IRQ.into()]
.set_handler_fn(serial_interrupt)
.set_stack_index(0);
}
interrupts::add_irq_name((SERIAL_IRQ - 32).try_into().unwrap(), "COM1");
interrupts::add_irq_name(SERIAL_IRQ - 32, "COM1");
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ pub mod fs;
pub mod io;
mod mm;
pub mod scheduler;
#[cfg(all(feature = "shell", target_arch = "x86_64"))]
mod shell;
mod synch;
pub mod syscalls;
pub mod time;
Expand Down Expand Up @@ -283,6 +285,8 @@ extern "C" fn initd(_arg: usize) {

syscalls::init();
fs::init();
#[cfg(all(feature = "shell", target_arch = "x86_64"))]
shell::init();

// Get the application arguments and environment variables.
#[cfg(not(test))]
Expand Down
55 changes: 55 additions & 0 deletions src/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use hermit_sync::Lazy;
use simple_shell::*;

use crate::arch::kernel::COM1;
use crate::interrupts::print_statistics;

fn read() -> Option<u8> {
COM1.lock().as_mut().map(|s| s.read())?
}

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

shell.commands.insert(
"help",
ShellCommand {
help: "Print this help message",
func: |_, shell| {
shell.print_help_screen();
Ok(())
},
aliases: &["?", "h"],
},
);
shell.commands.insert(
"interrupts",
ShellCommand {
help: "Shows the number of received interrupts",
func: |_, shell| {
print_statistics();
Ok(())
},
aliases: &["i"],
},
);
shell.commands.insert(
"shutdown",
ShellCommand {
help: "Shutdown HermitOS",
func: |_, shell| {
crate::__sys_shutdown(0);
Ok(())
},
aliases: &["s"],
},
);

shell
});

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

0 comments on commit 27556ba

Please sign in to comment.