implement canonical line discipline

This commit is contained in:
Jianfeng Jiang 2023-01-06 11:14:34 +08:00
parent 1e89933a99
commit 6b37f0f956
31 changed files with 641 additions and 192 deletions

View File

@ -3,4 +3,5 @@ We don't include the source code of busybox here since the source code is really
After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox
1. `make defconfig`. We set all config as default. 1. `make defconfig`. We set all config as default.
2. change the line in .config: `#CONFIG_STATIC is not set` => `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now. 2. Set static link option in .config: `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now.
3. Set standalone shell option in .config: `CONFIG_FEATURE_SH_STANDALONE=y`. The standalone ash will try to call busybox applets instead of search binaries in host system. e.g., when running ls, standalone ash will invoke `busybox ash`.

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:be0c152c1e47d3109f808cda876c3a90a1ef959007741e126086552e1063eede oid sha256:6db28e1ed8bdac06ac595b2126cefc10ecf16156bf4c1005950f561aedc0531a
size 2701792 size 2701792

View File

@ -15,6 +15,6 @@ pub const PAGE_SIZE_BITS: usize = 0xc;
pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS; pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS;
pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Close; pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Error;
/// This value represent the base timer frequency in Hz /// This value represent the base timer frequency in Hz
pub const TIMER_FREQ: u64 = 100; pub const TIMER_FREQ: u64 = 100;

View File

@ -1,19 +1,19 @@
//! Device-related APIs. //! Device-related APIs.
pub mod framebuffer; pub mod framebuffer;
pub mod console;
mod io_port; mod io_port;
pub mod pci; pub mod pci;
pub mod serial;
pub use self::io_port::IoPort; pub use self::io_port::IoPort;
/// first step to init device, call before the memory allocator init /// first step to init device, call before the memory allocator init
pub(crate) fn first_init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) { pub(crate) fn first_init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
framebuffer::init(framebuffer); framebuffer::init(framebuffer);
console::init(); serial::init();
} }
/// second step to init device, call after the memory allocator init /// second step to init device, call after the memory allocator init
pub(crate) fn second_init() { pub(crate) fn second_init() {
console::register_console_input_callback(|trap| {}); serial::register_serial_input_irq_handler(|trap| {});
} }

View File

@ -1,6 +1,8 @@
use alloc::{sync::Arc, vec::Vec};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use spin::Mutex;
use crate::{cell::Cell, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame}; use crate::{cell::Cell, debug, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame};
use core::fmt::{self, Write}; use core::fmt::{self, Write};
bitflags::bitflags! { bitflags::bitflags! {
@ -18,8 +20,13 @@ const SERIAL_LINE_CTRL: u16 = SERIAL_DATA + 3;
const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4; const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4;
const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5; const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5;
lazy_static! { lazy_static! {
static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> = static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> = {
Cell::new(pic::allocate_irq(4).unwrap()); let irq = Cell::new(pic::allocate_irq(4).unwrap());
irq.get().on_active(handle_serial_input);
irq
};
pub static ref SERIAL_INPUT_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
} }
/// Initializes the serial port. /// Initializes the serial port.
@ -43,13 +50,26 @@ pub(crate) fn init() {
out8(SERIAL_INT_EN, 0x01); out8(SERIAL_INT_EN, 0x01);
} }
pub fn register_console_input_callback<F>(callback: F) pub(crate) fn register_serial_input_irq_handler<F>(callback: F)
where where
F: Fn(&TrapFrame) + Sync + Send + 'static, F: Fn(&TrapFrame) + Sync + Send + 'static,
{ {
CONSOLE_IRQ_CALLBACK.get().on_active(callback); CONSOLE_IRQ_CALLBACK.get().on_active(callback);
} }
fn handle_serial_input(trap_frame: &TrapFrame) {
// debug!("keyboard interrupt was met");
if SERIAL_INPUT_CALLBACKS.is_locked() {
return;
}
let lock = SERIAL_INPUT_CALLBACKS.lock();
let received_char = receive_char().unwrap();
debug!("receive char = {:?}", received_char);
for callback in lock.iter() {
callback(received_char);
}
}
fn line_sts() -> LineSts { fn line_sts() -> LineSts {
LineSts::from_bits_truncate(in8(SERIAL_LINE_STS)) LineSts::from_bits_truncate(in8(SERIAL_LINE_STS))
} }
@ -99,13 +119,13 @@ pub fn print(args: fmt::Arguments) {
#[macro_export] #[macro_export]
macro_rules! console_print { macro_rules! console_print {
($fmt: literal $(, $($arg: tt)+)?) => { ($fmt: literal $(, $($arg: tt)+)?) => {
$crate::device::console::print(format_args!($fmt $(, $($arg)+)?)) $crate::device::serial::print(format_args!($fmt $(, $($arg)+)?))
} }
} }
#[macro_export] #[macro_export]
macro_rules! console_println { macro_rules! console_println {
($fmt: literal $(, $($arg: tt)+)?) => { ($fmt: literal $(, $($arg: tt)+)?) => {
$crate::device::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) $crate::device::serial::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
} }
} }

View File

@ -6,8 +6,8 @@ pub mod acpi;
pub mod apic; pub mod apic;
pub mod ioapic; pub mod ioapic;
pub mod pic; pub mod pic;
pub mod timer;
pub mod rtc; pub mod rtc;
pub mod timer;
pub use apic::ack; pub use apic::ack;
pub use timer::TimerCallback; pub use timer::TimerCallback;

View File

@ -1,52 +1,58 @@
use core::sync::atomic::AtomicU8; use core::sync::atomic::AtomicU8;
use core::sync::atomic::Ordering::Relaxed; use core::sync::atomic::Ordering::Relaxed;
use acpi::{sdt::Signature, fadt::Fadt}; use acpi::{fadt::Fadt, sdt::Signature};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use spin::Mutex; use spin::Mutex;
use crate::{x86_64_util::{out8, in8}, time::Time}; use crate::{
time::Time,
x86_64_util::{in8, out8},
};
use super::acpi::ACPI_TABLES; use super::acpi::ACPI_TABLES;
const CMOS_ADDRESS : u16 = 0x70; const CMOS_ADDRESS: u16 = 0x70;
const CMOS_DATA : u16 = 0x71; const CMOS_DATA: u16 = 0x71;
pub(crate) static CENTURY_REGISTER : AtomicU8 = AtomicU8::new(0); pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
lazy_static!{ lazy_static! {
static ref READ_TIME : Mutex<Time> = Mutex::new(Time::default()); static ref READ_TIME: Mutex<Time> = Mutex::new(Time::default());
} }
pub fn init(){ pub fn init() {
let c = ACPI_TABLES.lock(); let c = ACPI_TABLES.lock();
let r_century = unsafe{ let r_century = unsafe {
let a = c.get_sdt::<Fadt>(Signature::FADT).unwrap().expect("not found FACP in ACPI table"); let a = c
.get_sdt::<Fadt>(Signature::FADT)
.unwrap()
.expect("not found FACP in ACPI table");
a.century a.century
}; };
CENTURY_REGISTER.store(r_century, Relaxed); CENTURY_REGISTER.store(r_century, Relaxed);
} }
pub fn get_cmos(reg: u8) -> u8{ pub fn get_cmos(reg: u8) -> u8 {
out8(CMOS_ADDRESS, reg as u8); out8(CMOS_ADDRESS, reg as u8);
in8(CMOS_DATA) in8(CMOS_DATA)
} }
pub fn is_updating() -> bool{ pub fn is_updating() -> bool {
out8(CMOS_ADDRESS,0x0A); out8(CMOS_ADDRESS, 0x0A);
in8(CMOS_DATA) & 0x80 != 0 in8(CMOS_DATA) & 0x80 != 0
} }
pub fn read()-> Time{ pub fn read() -> Time {
update_time(); update_time();
READ_TIME.lock().clone() READ_TIME.lock().clone()
} }
/// read year,month,day and other data /// read year,month,day and other data
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers /// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
fn update_time(){ fn update_time() {
let mut last_time :Time; let mut last_time: Time;
let register_b : u8; let register_b: u8;
let mut lock = READ_TIME.lock(); let mut lock = READ_TIME.lock();
lock.update_from_rtc(); lock.update_from_rtc();
@ -55,18 +61,15 @@ fn update_time(){
lock.update_from_rtc(); lock.update_from_rtc();
while *lock != last_time {
while *lock!=last_time{
last_time = lock.clone(); last_time = lock.clone();
lock.update_from_rtc(); lock.update_from_rtc();
} }
register_b = get_cmos(0x0B); register_b = get_cmos(0x0B);
lock.convert_bcd_to_binary(register_b); lock.convert_bcd_to_binary(register_b);
lock.convert_12_hour_to_24_hour(register_b); lock.convert_12_hour_to_24_hour(register_b);
lock.modify_year(); lock.modify_year();
} }

View File

@ -23,13 +23,13 @@ pub(crate) mod mm;
pub mod prelude; pub mod prelude;
pub mod sync; pub mod sync;
pub mod task; pub mod task;
pub mod time;
pub mod timer; pub mod timer;
pub mod trap; pub mod trap;
pub mod user; pub mod user;
mod util; mod util;
pub mod vm; pub mod vm;
pub(crate) mod x86_64_util; pub(crate) mod x86_64_util;
pub mod time;
use core::{mem, panic::PanicInfo}; use core::{mem, panic::PanicInfo};
pub use driver::ack as apic_ack; pub use driver::ack as apic_ack;
@ -45,7 +45,7 @@ use bootloader::{
boot_info::{FrameBuffer, MemoryRegionKind}, boot_info::{FrameBuffer, MemoryRegionKind},
BootInfo, BootInfo,
}; };
pub use device::console::receive_char; pub use device::serial::receive_char;
pub use mm::address::{align_down, align_up, is_aligned, virt_to_phys}; pub use mm::address::{align_down, align_up, is_aligned, virt_to_phys};
pub use mm::page_table::translate_not_offset_virtual_address; pub use mm::page_table::translate_not_offset_virtual_address;
pub use trap::{allocate_irq, IrqAllocateHandle, TrapFrame}; pub use trap::{allocate_irq, IrqAllocateHandle, TrapFrame};

View File

@ -23,7 +23,7 @@ pub fn log_print(args: Arguments) {
#[cfg(feature = "serial_print")] #[cfg(feature = "serial_print")]
#[doc(hidden)] #[doc(hidden)]
pub fn log_print(args: Arguments) { pub fn log_print(args: Arguments) {
crate::device::console::print(args); crate::device::serial::print(args);
} }
/// This macro should not be directly called. /// This macro should not be directly called.

View File

@ -1,10 +1,8 @@
use crate::driver::rtc::{get_cmos, is_updating, CENTURY_REGISTER, read}; use crate::driver::rtc::{get_cmos, is_updating, read, CENTURY_REGISTER};
use core::sync::atomic::Ordering::Relaxed; use core::sync::atomic::Ordering::Relaxed;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time {
#[derive(Debug,Clone, Copy,Default,PartialEq, Eq, PartialOrd, Ord)]
pub struct Time{
century: u8, century: u8,
pub year: u16, pub year: u16,
pub month: u8, pub month: u8,
@ -14,9 +12,9 @@ pub struct Time{
pub second: u8, pub second: u8,
} }
impl Time{ impl Time {
pub(crate) fn update_from_rtc(&mut self){ pub(crate) fn update_from_rtc(&mut self) {
while is_updating(){} while is_updating() {}
self.second = get_cmos(0x00); self.second = get_cmos(0x00);
self.minute = get_cmos(0x02); self.minute = get_cmos(0x02);
self.hour = get_cmos(0x04); self.hour = get_cmos(0x04);
@ -26,19 +24,20 @@ impl Time{
let century_register = CENTURY_REGISTER.load(Relaxed); let century_register = CENTURY_REGISTER.load(Relaxed);
if century_register !=0{ if century_register != 0 {
self.century = get_cmos(century_register); self.century = get_cmos(century_register);
} }
} }
/// convert BCD to binary values /// convert BCD to binary values
/// ref:https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers /// ref:https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
pub(crate) fn convert_bcd_to_binary(&mut self,register_b: u8){ pub(crate) fn convert_bcd_to_binary(&mut self, register_b: u8) {
if register_b & 0x04 == 0{ if register_b & 0x04 == 0 {
let century_register = CENTURY_REGISTER.load(Relaxed); let century_register = CENTURY_REGISTER.load(Relaxed);
self.second = (self.second & 0x0F) + ((self.second / 16) * 10); self.second = (self.second & 0x0F) + ((self.second / 16) * 10);
self.minute = (self.minute & 0x0F) + ((self.minute / 16) * 10); self.minute = (self.minute & 0x0F) + ((self.minute / 16) * 10);
self.hour = ( (self.hour & 0x0F) + (((self.hour & 0x70) / 16) * 10) ) | (self.hour & 0x80); self.hour =
((self.hour & 0x0F) + (((self.hour & 0x70) / 16) * 10)) | (self.hour & 0x80);
self.day = (self.day & 0x0F) + ((self.day / 16) * 10); self.day = (self.day & 0x0F) + ((self.day / 16) * 10);
self.month = (self.month & 0x0F) + ((self.month / 16) * 10); self.month = (self.month & 0x0F) + ((self.month / 16) * 10);
self.year = (self.year & 0x0F) + ((self.year / 16) * 10); self.year = (self.year & 0x0F) + ((self.year / 16) * 10);
@ -48,28 +47,26 @@ impl Time{
} }
} }
/// convert 12 hour clock to 24 hour clock /// convert 12 hour clock to 24 hour clock
pub(crate) fn convert_12_hour_to_24_hour(&mut self,register_b:u8){ pub(crate) fn convert_12_hour_to_24_hour(&mut self, register_b: u8) {
// bit1 in register_b is not set if 12 hour format is enable // bit1 in register_b is not set if 12 hour format is enable
// if highest bit in hour is set, then it is pm // if highest bit in hour is set, then it is pm
if ((register_b & 0x02)==0) && ((self.hour & 0x80) !=0){ if ((register_b & 0x02) == 0) && ((self.hour & 0x80) != 0) {
self.hour = ((self.hour & 0x7F) + 12) % 24; self.hour = ((self.hour & 0x7F) + 12) % 24;
} }
} }
/// convert raw year (10, 20 etc.) to real year (2010, 2020 etc.) /// convert raw year (10, 20 etc.) to real year (2010, 2020 etc.)
pub(crate) fn modify_year(&mut self){ pub(crate) fn modify_year(&mut self) {
let century_register = CENTURY_REGISTER.load(Relaxed); let century_register = CENTURY_REGISTER.load(Relaxed);
if century_register !=0{ if century_register != 0 {
self.year += self.century as u16 * 100; self.year += self.century as u16 * 100;
}else{ } else {
panic!("century register not exists"); panic!("century register not exists");
} }
} }
} }
/// get real time /// get real time
pub fn get_real_time() -> Time{ pub fn get_real_time() -> Time {
read() read()
} }

View File

@ -172,7 +172,7 @@ pub(crate) fn init() {
model_specific::SFMask::write( model_specific::SFMask::write(
RFlags::TRAP_FLAG RFlags::TRAP_FLAG
| RFlags::DIRECTION_FLAG | RFlags::DIRECTION_FLAG
| RFlags::INTERRUPT_FLAG // | RFlags::INTERRUPT_FLAG
| RFlags::IOPL_LOW | RFlags::IOPL_LOW
| RFlags::IOPL_HIGH | RFlags::IOPL_HIGH
| RFlags::NESTED_TASK | RFlags::NESTED_TASK

View File

@ -145,4 +145,3 @@ fn run_test_command(mut cmd: Command) -> anyhow::Result<ExitStatus> {
let status = runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS))?; let status = runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS))?;
Ok(status) Ok(status)
} }

View File

@ -1,54 +0,0 @@
use jinux_frame::println;
use alloc::{sync::Arc, vec::Vec};
use jinux_frame::{TrapFrame, receive_char, info};
use lazy_static::lazy_static;
use spin::Mutex;
use crate::{process::Process, current};
lazy_static! {
static ref KEYBOARD_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
static ref WAIT_INPUT_PROCESS : Mutex<Option<Arc<Process>>> = Mutex::new(None);
}
pub fn init() {
jinux_frame::device::console::register_console_input_callback(handle_irq);
register_console_callback(Arc::new(console_receive_callback));
}
fn handle_irq(trap_frame: &TrapFrame) {
if KEYBOARD_CALLBACKS.is_locked() {
return;
}
let lock = KEYBOARD_CALLBACKS.lock();
for callback in lock.iter() {
callback.call(((jinux_frame::device::console::receive_char().unwrap()),));
}
}
pub fn register_console_callback(callback: Arc<dyn Fn(u8) + 'static + Send + Sync>) {
KEYBOARD_CALLBACKS.lock().push(callback);
}
fn console_receive_callback(data: u8){
let process = WAIT_INPUT_PROCESS.lock().take();
process.unwrap().send_to_scheduler();
}
/// receive char from console, if there is no data in buffer, then it will switch to other task
/// until it is notified.
pub fn receive_console_char() -> u8{
loop{
if let Some(byte) = receive_char() {
return byte;
}else if WAIT_INPUT_PROCESS.lock().is_none(){
WAIT_INPUT_PROCESS.lock().replace(current!());
Process::yield_now();
WAIT_INPUT_PROCESS.lock().take();
}else{
panic!("there is process waiting in the console receive list!");
}
}
}

View File

@ -1,7 +1,7 @@
pub mod console;
pub mod pci; pub mod pci;
pub mod tty;
pub fn init() { pub fn init() {
pci::init(); pci::init();
console::init(); tty::init();
} }

View File

@ -0,0 +1,80 @@
use jinux_frame::device::serial::SERIAL_INPUT_CALLBACKS;
use crate::{
prelude::*,
tty::{get_n_tty, Tty},
};
lazy_static! {
pub static ref TTY_DRIVER: Arc<TtyDriver> = {
let tty_driver = Arc::new(TtyDriver::new());
// FIXME: install n_tty into tty_driver?
let n_tty = get_n_tty();
tty_driver.install(n_tty.clone());
tty_driver
};
}
pub struct TtyDriver {
ttys: Mutex<Vec<Arc<Tty>>>,
}
impl TtyDriver {
pub fn new() -> Self {
Self {
ttys: Mutex::new(Vec::new()),
}
}
/// Return the tty device in driver's internal table.
pub fn lookup(&self, index: usize) -> Result<Arc<Tty>> {
let ttys = self.ttys.lock();
// Return the tty device corresponding to idx
if index >= ttys.len() {
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
}
let tty = ttys[index].clone();
drop(ttys);
Ok(tty)
}
/// Install a new tty into the driver's internal tables.
pub fn install(self: &Arc<Self>, tty: Arc<Tty>) {
tty.set_driver(Arc::downgrade(self));
self.ttys.lock().push(tty);
}
/// remove a new tty into the driver's internal tables.
pub fn remove(&self, index: usize) -> Result<()> {
let mut ttys = self.ttys.lock();
if index >= ttys.len() {
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
}
let removed_tty = ttys.remove(index);
removed_tty.set_driver(Weak::new());
drop(ttys);
Ok(())
}
pub fn receive_char(&self, item: u8) {
// FIXME: should the char send to all ttys?
for tty in &*self.ttys.lock() {
tty.receive_char(item);
}
}
}
fn serial_input_callback(item: u8) {
let tty_driver = get_tty_driver();
tty_driver.receive_char(item);
}
fn get_tty_driver() -> &'static TtyDriver {
&TTY_DRIVER
}
pub fn init() {
SERIAL_INPUT_CALLBACKS
.lock()
.push(Arc::new(serial_input_callback));
}

View File

@ -1,5 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use crate::tty::get_console; use crate::tty::get_n_tty;
use core::any::Any; use core::any::Any;
use super::events::IoEvents; use super::events::IoEvents;
@ -21,7 +21,7 @@ pub trait File: Send + Sync + Any {
match cmd { match cmd {
IoctlCmd::TCGETS => { IoctlCmd::TCGETS => {
// FIXME: only a work around // FIXME: only a work around
let tty = get_console(); let tty = get_n_tty();
tty.ioctl(cmd, arg) tty.ioctl(cmd, arg)
} }
_ => panic!("Ioctl unsupported"), _ => panic!("Ioctl unsupported"),

View File

@ -67,4 +67,14 @@ impl Stat {
stat.st_blksize = 4096; stat.st_blksize = 4096;
stat stat
} }
// Fake stat for /bin/stty
pub fn fake_stty_stat() -> Self {
let mut stat = Stat::default();
stat.st_mode = S_IFREG | 0o755;
stat.st_nlink = 1;
stat.st_blksize = 4096;
stat.st_size = 84344;
stat
}
} }

View File

@ -1,6 +1,6 @@
use super::events::IoEvents; use super::events::IoEvents;
use crate::prelude::*; use crate::prelude::*;
use crate::tty::{get_console, Tty}; use crate::tty::{get_n_tty, Tty};
use super::file::{File, FileDescripter}; use super::file::{File, FileDescripter};
@ -10,23 +10,19 @@ pub const FD_STDERR: FileDescripter = 2;
pub struct Stdin { pub struct Stdin {
console: Option<Arc<Tty>>, console: Option<Arc<Tty>>,
bind_to_console: bool,
} }
pub struct Stdout { pub struct Stdout {
console: Option<Arc<Tty>>, console: Option<Arc<Tty>>,
bind_to_console: bool,
} }
pub struct Stderr { pub struct Stderr {
console: Option<Arc<Tty>>, console: Option<Arc<Tty>>,
bind_to_console: bool,
} }
impl File for Stdin { impl File for Stdin {
fn poll(&self) -> IoEvents { fn poll(&self) -> IoEvents {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.poll() console.poll()
} else { } else {
todo!() todo!()
@ -34,8 +30,7 @@ impl File for Stdin {
} }
fn read(&self, buf: &mut [u8]) -> Result<usize> { fn read(&self, buf: &mut [u8]) -> Result<usize> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.read(buf) console.read(buf)
} else { } else {
todo!() todo!()
@ -43,8 +38,7 @@ impl File for Stdin {
} }
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> { fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.ioctl(cmd, arg) console.ioctl(cmd, arg)
} else { } else {
todo!() todo!()
@ -53,8 +47,7 @@ impl File for Stdin {
} }
impl File for Stdout { impl File for Stdout {
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> { fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.ioctl(cmd, arg) console.ioctl(cmd, arg)
} else { } else {
todo!() todo!()
@ -62,8 +55,7 @@ impl File for Stdout {
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.write(buf) console.write(buf)
} else { } else {
todo!() todo!()
@ -73,8 +65,7 @@ impl File for Stdout {
impl File for Stderr { impl File for Stderr {
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> { fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.ioctl(cmd, arg) console.ioctl(cmd, arg)
} else { } else {
todo!() todo!()
@ -82,8 +73,7 @@ impl File for Stderr {
} }
fn write(&self, buf: &[u8]) -> Result<usize> { fn write(&self, buf: &[u8]) -> Result<usize> {
if self.bind_to_console { if let Some(console) = self.console.as_ref() {
let console = self.console.as_ref().unwrap();
console.write(buf) console.write(buf)
} else { } else {
todo!() todo!()
@ -95,10 +85,9 @@ impl Stdin {
/// FIXME: console should be file under devfs. /// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled. /// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self { pub fn new_with_default_console() -> Self {
let console = get_console(); let console = get_n_tty();
Self { Self {
console: Some(console.clone()), console: Some(console.clone()),
bind_to_console: true,
} }
} }
} }
@ -107,10 +96,9 @@ impl Stdout {
/// FIXME: console should be file under devfs. /// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled. /// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self { pub fn new_with_default_console() -> Self {
let console = get_console(); let console = get_n_tty();
Self { Self {
console: Some(console.clone()), console: Some(console.clone()),
bind_to_console: true,
} }
} }
} }
@ -119,10 +107,9 @@ impl Stderr {
/// FIXME: console should be file under devfs. /// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled. /// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self { pub fn new_with_default_console() -> Self {
let console = get_console(); let console = get_n_tty();
Self { Self {
console: Some(console.clone()), console: Some(console.clone()),
bind_to_console: true,
} }
} }
} }

View File

@ -15,7 +15,7 @@ use crate::prelude::*;
use crate::rights::Full; use crate::rights::Full;
use crate::thread::kernel_thread::KernelThreadExt; use crate::thread::kernel_thread::KernelThreadExt;
use crate::thread::{thread_table, Thread}; use crate::thread::{thread_table, Thread};
use crate::tty::get_console; use crate::tty::get_n_tty;
use crate::vm::vmar::Vmar; use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue; use jinux_frame::sync::WaitQueue;
use jinux_frame::task::Task; use jinux_frame::task::Task;
@ -149,7 +149,9 @@ impl Process {
let process = Process::create_user_process(filename, elf_file_content, argv, envp); let process = Process::create_user_process(filename, elf_file_content, argv, envp);
// FIXME: How to determine the fg process group? // FIXME: How to determine the fg process group?
let pgid = process.pgid(); let pgid = process.pgid();
get_console().set_fg(pgid); // FIXME: tty should be a parameter?
let tty = get_n_tty();
tty.set_fg(pgid);
process.run(); process.run();
process process
} }

View File

@ -1,4 +1,4 @@
use super::{process_table, Pgid, Pid, Process}; use super::{process_table, signal::signals::kernel::KernelSignal, Pgid, Pid, Process};
use crate::prelude::*; use crate::prelude::*;
pub struct ProcessGroup { pub struct ProcessGroup {
@ -43,6 +43,10 @@ impl ProcessGroup {
self.inner.lock().processes.insert(process.pid(), process); self.inner.lock().processes.insert(process.pid(), process);
} }
pub fn contains_process(&self, pid: Pid) -> bool {
self.inner.lock().processes.contains_key(&pid)
}
/// remove a process from this process group. /// remove a process from this process group.
/// If this group contains no processes now, the group itself will be deleted from global table. /// If this group contains no processes now, the group itself will be deleted from global table.
pub fn remove_process(&self, pid: Pid) { pub fn remove_process(&self, pid: Pid) {
@ -68,4 +72,14 @@ impl ProcessGroup {
process.poll_queue().wake_all(); process.poll_queue().wake_all();
} }
} }
/// send kernel signal to all processes in the group
pub fn kernel_signal(&self, signal: KernelSignal) {
for (_, process) in &self.inner.lock().processes {
process
.sig_queues()
.lock()
.enqueue(Box::new(signal.clone()));
}
}
} }

View File

@ -44,6 +44,7 @@ use crate::syscall::sched_yield::sys_sched_yield;
use crate::syscall::set_robust_list::sys_set_robust_list; use crate::syscall::set_robust_list::sys_set_robust_list;
use crate::syscall::set_tid_address::sys_set_tid_address; use crate::syscall::set_tid_address::sys_set_tid_address;
use crate::syscall::setpgid::sys_setpgid; use crate::syscall::setpgid::sys_setpgid;
use crate::syscall::stat::sys_stat;
use crate::syscall::tgkill::sys_tgkill; use crate::syscall::tgkill::sys_tgkill;
use crate::syscall::uname::sys_uname; use crate::syscall::uname::sys_uname;
use crate::syscall::wait4::sys_wait4; use crate::syscall::wait4::sys_wait4;
@ -96,6 +97,7 @@ mod sched_yield;
mod set_robust_list; mod set_robust_list;
mod set_tid_address; mod set_tid_address;
mod setpgid; mod setpgid;
mod stat;
mod tgkill; mod tgkill;
mod uname; mod uname;
mod wait4; mod wait4;
@ -137,6 +139,7 @@ define_syscall_nums!(
SYS_READ = 0, SYS_READ = 0,
SYS_WRITE = 1, SYS_WRITE = 1,
SYS_CLOSE = 3, SYS_CLOSE = 3,
SYS_STAT = 4,
SYS_FSTAT = 5, SYS_FSTAT = 5,
SYS_LSTAT = 6, SYS_LSTAT = 6,
SYS_POLL = 7, SYS_POLL = 7,
@ -244,6 +247,7 @@ pub fn syscall_dispatch(
SYS_READ => syscall_handler!(3, sys_read, args), SYS_READ => syscall_handler!(3, sys_read, args),
SYS_WRITE => syscall_handler!(3, sys_write, args), SYS_WRITE => syscall_handler!(3, sys_write, args),
SYS_CLOSE => syscall_handler!(1, sys_close, args), SYS_CLOSE => syscall_handler!(1, sys_close, args),
SYS_STAT => syscall_handler!(2, sys_stat, args),
SYS_FSTAT => syscall_handler!(2, sys_fstat, args), SYS_FSTAT => syscall_handler!(2, sys_fstat, args),
SYS_LSTAT => syscall_handler!(2, sys_lstat, args), SYS_LSTAT => syscall_handler!(2, sys_lstat, args),
SYS_POLL => syscall_handler!(3, sys_poll, args), SYS_POLL => syscall_handler!(3, sys_poll, args),
@ -290,7 +294,10 @@ pub fn syscall_dispatch(
SYS_OPENAT => syscall_handler!(4, sys_openat, args), SYS_OPENAT => syscall_handler!(4, sys_openat, args),
SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args), SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args),
SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args), SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args),
_ => panic!("Unsupported syscall number: {}", syscall_number), _ => {
error!("Unimplemented syscall number: {}", syscall_number);
return_errno_with_message!(Errno::ENOSYS, "Syscall was unimplemented");
}
} }
} }

View File

@ -4,7 +4,7 @@ use crate::fs::file_handle::FileHandle;
use crate::log_syscall_entry; use crate::log_syscall_entry;
use crate::prelude::*; use crate::prelude::*;
use crate::syscall::constants::MAX_FILENAME_LEN; use crate::syscall::constants::MAX_FILENAME_LEN;
use crate::tty::get_console; use crate::tty::get_n_tty;
use crate::util::read_cstring_from_user; use crate::util::read_cstring_from_user;
use super::SyscallReturn; use super::SyscallReturn;
@ -38,11 +38,16 @@ pub fn sys_openat(
} }
if dirfd == AT_FDCWD && pathname == CString::new("./trace")? { if dirfd == AT_FDCWD && pathname == CString::new("./trace")? {
return_errno_with_message!(Errno::ENOENT, "No such file"); // Debug use: This file is used for output busybox log
let trace_file = FileHandle::new_file(Arc::new(BusyBoxTraceFile) as Arc<dyn File>);
let current = current!();
let mut file_table = current.file_table().lock();
let fd = file_table.insert(trace_file);
return Ok(SyscallReturn::Return(fd as _));
} }
if dirfd == AT_FDCWD && pathname == CString::new("/dev/tty")? { if dirfd == AT_FDCWD && pathname == CString::new("/dev/tty")? {
let tty_file = FileHandle::new_file(get_console().clone() as Arc<dyn File>); let tty_file = FileHandle::new_file(get_n_tty().clone() as Arc<dyn File>);
let current = current!(); let current = current!();
let mut file_table = current.file_table().lock(); let mut file_table = current.file_table().lock();
let fd = file_table.insert(tty_file); let fd = file_table.insert(tty_file);
@ -50,3 +55,13 @@ pub fn sys_openat(
} }
todo!() todo!()
} }
/// File for output busybox ash log.
struct BusyBoxTraceFile;
impl File for BusyBoxTraceFile {
fn write(&self, buf: &[u8]) -> Result<usize> {
debug!("ASH TRACE: {}", core::str::from_utf8(buf)?);
Ok(buf.len())
}
}

View File

@ -27,6 +27,11 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
return_errno_with_message!(Errno::EPERM, "process group must exist"); return_errno_with_message!(Errno::EPERM, "process group must exist");
} }
// if the process already belongs to the process group
if current.pgid() == pgid {
return Ok(SyscallReturn::Return(0));
}
if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) { if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) {
new_process_group.add_process(current.clone()); new_process_group.add_process(current.clone());
current.set_process_group(Arc::downgrade(&new_process_group)); current.set_process_group(Arc::downgrade(&new_process_group));

View File

@ -0,0 +1,16 @@
use super::SYS_STAT;
use crate::syscall::constants::MAX_FILENAME_LEN;
use crate::util::read_cstring_from_user;
use crate::{log_syscall_entry, prelude::*};
use super::SyscallReturn;
pub fn sys_stat(filename_ptr: Vaddr, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_STAT);
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
debug!(
"filename = {:?}, stat_buf_ptr = 0x{:x}",
filename, stat_buf_ptr
);
return_errno_with_message!(Errno::ENOSYS, "Stat is unimplemented");
}

View File

@ -25,6 +25,7 @@ pub fn sys_write(
let file = file_table.get_file(fd)?; let file = file_table.get_file(fd)?;
let mut buffer = vec![0u8; user_buf_len]; let mut buffer = vec![0u8; user_buf_len];
read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?; read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?;
debug!("write content = {:?}", buffer);
let write_len = file.write(&buffer)?; let write_len = file.write(&buffer)?;
Ok(SyscallReturn::Return(write_len as _)) Ok(SyscallReturn::Return(write_len as _))
} }

View File

@ -1,58 +1,256 @@
use crate::{prelude::*, process::Pgid}; use crate::fs::events::IoEvents;
use crate::process::signal::constants::{SIGINT, SIGQUIT};
use crate::{
prelude::*,
process::{process_table, signal::signals::kernel::KernelSignal, Pgid},
};
use jinux_frame::sync::WaitQueue;
use ringbuffer::{ConstGenericRingBuffer, RingBuffer, RingBufferRead, RingBufferWrite}; use ringbuffer::{ConstGenericRingBuffer, RingBuffer, RingBufferRead, RingBufferWrite};
use super::termio::KernelTermios; use super::termio::{KernelTermios, CC_C_CHAR};
// This implementation refers the implementation of linux // This implementation refers the implementation of linux
// https://elixir.bootlin.com/linux/latest/source/include/linux/tty_ldisc.h // https://elixir.bootlin.com/linux/latest/source/include/linux/tty_ldisc.h
const BUFFER_CAPACITY: usize = 4096; const BUFFER_CAPACITY: usize = 4096;
#[derive(Debug)]
pub struct LineDiscipline { pub struct LineDiscipline {
/// The write buffer /// current line
buffer: ConstGenericRingBuffer<u8, BUFFER_CAPACITY>, current_line: CurrentLine,
/// The read buffer
read_buffer: Mutex<ConstGenericRingBuffer<u8, BUFFER_CAPACITY>>,
/// The foreground process group /// The foreground process group
foreground: Option<Pgid>, foreground: Option<Pgid>,
/// termios /// termios
termios: KernelTermios, termios: KernelTermios,
/// wait until self is readable
read_wait_queue: WaitQueue,
}
#[derive(Debug)]
pub struct CurrentLine {
buffer: ConstGenericRingBuffer<u8, BUFFER_CAPACITY>,
}
impl CurrentLine {
pub fn new() -> Self {
Self {
buffer: ConstGenericRingBuffer::new(),
}
}
/// read all bytes inside current line and clear current line
pub fn drain(&mut self) -> Vec<u8> {
self.buffer.drain().collect()
}
pub fn push_char(&mut self, char: u8) {
// What should we do if line is full?
debug_assert!(!self.is_full());
self.buffer.push(char);
}
pub fn backspace(&mut self) {
self.buffer.dequeue();
}
pub fn is_full(&self) -> bool {
self.buffer.is_full()
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
} }
impl LineDiscipline { impl LineDiscipline {
/// create a new line discipline /// create a new line discipline
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
buffer: ConstGenericRingBuffer::new(), current_line: CurrentLine::new(),
read_buffer: Mutex::new(ConstGenericRingBuffer::new()),
foreground: None, foreground: None,
termios: KernelTermios::default(), termios: KernelTermios::default(),
read_wait_queue: WaitQueue::new(),
} }
} }
/// push char to buffer /// push char to line discipline. This function should be called in input interrupt handler.
pub fn push_char(&mut self, mut item: u8) { pub fn push_char(&mut self, mut item: u8) {
if self.termios.is_cooked_mode() {
todo!("We only support raw mode now. Cooked mode will be supported further.");
}
if self.termios.contains_icrnl() { if self.termios.contains_icrnl() {
if item == b'\r' { if item == b'\r' {
item = b'\n' item = b'\n'
} }
} }
self.buffer.push(item); if self.termios.is_canonical_mode() {
if item == *self.termios.get_special_char(CC_C_CHAR::VINTR) {
// type Ctrl + C, signal SIGINT
if self.termios.contains_isig() {
if let Some(fg) = self.foreground {
let kernel_signal = KernelSignal::new(SIGINT);
let fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal);
}
}
} else if item == *self.termios.get_special_char(CC_C_CHAR::VQUIT) {
// type Ctrl + \, signal SIGQUIT
if self.termios.contains_isig() {
if let Some(fg) = self.foreground {
let kernel_signal = KernelSignal::new(SIGQUIT);
let fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal);
}
}
} else if item == *self.termios.get_special_char(CC_C_CHAR::VKILL) {
// erase current line
self.current_line.drain();
} else if item == *self.termios.get_special_char(CC_C_CHAR::VERASE) {
// type backspace
if !self.current_line.is_empty() {
self.current_line.backspace();
}
} else if meet_new_line(item, &self.get_termios()) {
// a new line was met. We currently add the item to buffer.
// when we read content, the item should be skipped if it's EOF.
self.current_line.push_char(item);
let current_line_chars = self.current_line.drain();
for char in current_line_chars {
self.read_buffer.lock().push(char);
}
} else if item >= 0x20 && item < 0x7f {
// printable character
self.current_line.push_char(item);
}
} else {
// raw mode
self.read_buffer.lock().push(item);
// debug!("push char: {}", char::from(item))
}
if self.termios.contain_echo() {
self.output_char(item);
}
if self.is_readable() {
self.read_wait_queue.wake_all();
}
}
/// whether self is readable
fn is_readable(&self) -> bool {
self.read_buffer.lock().len() > 0
}
// TODO: respect output flags
fn output_char(&self, item: u8) {
if 0x20 <= item && item < 0x7f {
let ch = char::from(item);
print!("{}", ch);
}
if item == *self.termios.get_special_char(CC_C_CHAR::VERASE) {
// write a space to overwrite current character
let bytes: [u8; 3] = [b'\x08', b' ', b'\x08'];
let backspace = core::str::from_utf8(&bytes).unwrap();
print!("{}", backspace);
}
if self.termios.contains_echo_ctl() {
// The unprintable chars between 1-31 are mapped to ctrl characters between 65-95.
// e.g., 0x3 is mapped to 0x43, which is C. So, we will print ^C when 0x3 is met.
if 0 < item && item < 0x20 {
let ctrl_char_ascii = item + 0x40;
let ctrl_char = char::from(ctrl_char_ascii);
print!("^{ctrl_char}");
}
}
} }
/// read all bytes buffered to dst, return the actual read length. /// read all bytes buffered to dst, return the actual read length.
pub fn read(&mut self, dst: &mut [u8]) -> Result<usize> { pub fn read(&mut self, dst: &mut [u8]) -> Result<usize> {
let len = self.buffer.len(); let vmin = *self.termios.get_special_char(CC_C_CHAR::VMIN);
let read_len = len.min(dst.len()); let vtime = *self.termios.get_special_char(CC_C_CHAR::VTIME);
for i in 0..read_len { let read_len: usize = self.read_wait_queue.wait_until(|| {
if let Some(content) = self.buffer.dequeue() { // if current process does not belong to foreground process group,
dst[i] = content; // block until current process become foreground.
if !self.current_belongs_to_foreground() {
warn!("current process does not belong to foreground process group");
return None;
}
let len = self.read_buffer.lock().len();
let max_read_len = len.min(dst.len());
if vmin == 0 && vtime == 0 {
// poll read
return self.poll_read(dst);
}
if vmin > 0 && vtime == 0 {
// block read
return self.block_read(dst, vmin);
}
if vmin == 0 && vtime > 0 {
todo!()
}
if vmin > 0 && vtime > 0 {
todo!()
}
unreachable!()
});
Ok(read_len)
}
pub fn poll(&self) -> IoEvents {
if self.is_empty() {
IoEvents::empty()
} else {
IoEvents::POLLIN
}
}
/// returns immediately with the lesser of the number of bytes available or the number of bytes requested.
/// If no bytes are available, completes immediately, returning 0.
fn poll_read(&self, dst: &mut [u8]) -> Option<usize> {
let mut buffer = self.read_buffer.lock();
let len = buffer.len();
let max_read_len = len.min(dst.len());
if max_read_len == 0 {
return Some(0);
}
let mut read_len = 0;
for i in 0..max_read_len {
if let Some(next_char) = buffer.dequeue() {
if self.termios.is_canonical_mode() {
// canonical mode, read until meet new line
if meet_new_line(next_char, self.get_termios()) {
if !should_not_be_read(next_char, self.get_termios()) {
dst[i] = next_char;
read_len += 1;
}
break;
} else {
dst[i] = next_char;
read_len += 1;
}
} else {
// raw mode
// FIXME: avoid addtional bound check
dst[i] = next_char;
read_len += 1;
}
} else { } else {
break; break;
} }
} }
Ok(read_len)
Some(read_len)
}
// The read() blocks until the lesser of the number of bytes requested or
// MIN bytes are available, and returns the lesser of the two values.
pub fn block_read(&self, dst: &mut [u8], vmin: u8) -> Option<usize> {
let min_read_len = (vmin as usize).min(dst.len());
let buffer_len = self.read_buffer.lock().len();
if buffer_len < min_read_len {
return None;
}
return self.poll_read(&mut dst[..min_read_len]);
} }
/// write bytes to buffer, if flush to console, then write the content to console /// write bytes to buffer, if flush to console, then write the content to console
@ -60,9 +258,27 @@ impl LineDiscipline {
todo!() todo!()
} }
/// whether the current process belongs to foreground process group
fn current_belongs_to_foreground(&self) -> bool {
let current = current!();
if let Some(fg_pgid) = self.foreground {
if let Some(process_group) = process_table::pgid_to_process_group(fg_pgid) {
if process_group.contains_process(current.pid()) {
return true;
}
}
}
false
}
/// set foreground process group /// set foreground process group
pub fn set_fg(&mut self, fg_pgid: Pgid) { pub fn set_fg(&mut self, fg_pgid: Pgid) {
self.foreground = Some(fg_pgid); self.foreground = Some(fg_pgid);
// Some background processes may be waiting on the wait queue, when set_fg, the background processes may be able to read.
if self.is_readable() {
self.read_wait_queue.wake_all();
}
} }
/// get foreground process group id /// get foreground process group id
@ -72,7 +288,7 @@ impl LineDiscipline {
/// whether there is buffered data /// whether there is buffered data
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.buffer.len() == 0 self.read_buffer.lock().len() == 0
} }
pub fn get_termios(&self) -> &KernelTermios { pub fn get_termios(&self) -> &KernelTermios {
@ -83,3 +299,27 @@ impl LineDiscipline {
self.termios = termios; self.termios = termios;
} }
} }
fn meet_new_line(item: u8, termios: &KernelTermios) -> bool {
if item == b'\n'
|| item == *termios.get_special_char(CC_C_CHAR::VEOF)
|| item == *termios.get_special_char(CC_C_CHAR::VEOL)
{
return true;
}
if termios.contains_iexten() && item == *termios.get_special_char(CC_C_CHAR::VEOL2) {
return true;
}
false
}
/// The special char should not be read by reading process
fn should_not_be_read(item: u8, termios: &KernelTermios) -> bool {
if item == *termios.get_special_char(CC_C_CHAR::VEOF) {
true
} else {
false
}
}

View File

@ -1,7 +1,7 @@
use self::line_discipline::LineDiscipline; use self::line_discipline::LineDiscipline;
use crate::driver::console::receive_console_char; use crate::driver::tty::TtyDriver;
use crate::fs::events::IoEvents; use crate::fs::events::IoEvents;
use crate::fs::ioctl::IoctlCmd; use crate::fs::ioctl::IoctlCmd;
use crate::process::{process_table, Pgid}; use crate::process::{process_table, Pgid};
use crate::util::{read_val_from_user, write_val_to_user}; use crate::util::{read_val_from_user, write_val_to_user};
@ -22,6 +22,8 @@ pub struct Tty {
name: CString, name: CString,
/// line discipline /// line discipline
ldisc: Mutex<LineDiscipline>, ldisc: Mutex<LineDiscipline>,
/// driver
driver: Mutex<Weak<TtyDriver>>,
} }
impl Tty { impl Tty {
@ -29,6 +31,7 @@ impl Tty {
Tty { Tty {
name, name,
ldisc: Mutex::new(LineDiscipline::new()), ldisc: Mutex::new(LineDiscipline::new()),
driver: Mutex::new(Weak::new()),
} }
} }
@ -37,9 +40,13 @@ impl Tty {
self.ldisc.lock().set_fg(pgid); self.ldisc.lock().set_fg(pgid);
} }
pub fn set_driver(&self, driver: Weak<TtyDriver>) {
*self.driver.lock() = driver;
}
/// Wake up foreground process group that wait on IO events. /// Wake up foreground process group that wait on IO events.
/// This function should be called when the interrupt handler of IO events is called. /// This function should be called when the interrupt handler of IO events is called.
pub fn wake_fg_proc_grp(&self) { fn wake_fg_proc_grp(&self) {
let ldisc = self.ldisc.lock(); let ldisc = self.ldisc.lock();
if let Some(fg_pgid) = ldisc.get_fg() { if let Some(fg_pgid) = ldisc.get_fg() {
if let Some(fg_proc_grp) = process_table::pgid_to_process_group(*fg_pgid) { if let Some(fg_proc_grp) = process_table::pgid_to_process_group(*fg_pgid) {
@ -47,6 +54,11 @@ impl Tty {
} }
} }
} }
pub fn receive_char(&self, item: u8) {
self.ldisc.lock().push_char(item);
self.wake_fg_proc_grp();
}
} }
impl File for Tty { impl File for Tty {
@ -64,13 +76,7 @@ impl File for Tty {
} }
fn poll(&self) -> IoEvents { fn poll(&self) -> IoEvents {
if !self.ldisc.lock().is_empty() { self.ldisc.lock().poll()
return IoEvents::POLLIN;
}
// receive keyboard input
let byte = receive_console_char();
self.ldisc.lock().push_char(byte);
return IoEvents::POLLIN;
} }
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> { fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
@ -121,6 +127,6 @@ impl File for Tty {
} }
/// FIXME: should we maintain a static console? /// FIXME: should we maintain a static console?
pub fn get_console() -> &'static Arc<Tty> { pub fn get_n_tty() -> &'static Arc<Tty> {
&N_TTY &N_TTY
} }

View File

@ -113,27 +113,103 @@ pub enum CC_C_CHAR {
VEOL2 = 16, VEOL2 = 16,
} }
impl CC_C_CHAR {
// The special char is the same as ubuntu
pub fn char(&self) -> u8 {
match self {
CC_C_CHAR::VINTR => 3,
CC_C_CHAR::VQUIT => 28,
CC_C_CHAR::VERASE => 127,
CC_C_CHAR::VKILL => 21,
CC_C_CHAR::VEOF => 4,
CC_C_CHAR::VTIME => 0,
CC_C_CHAR::VMIN => 1,
CC_C_CHAR::VSWTC => 0,
CC_C_CHAR::VSTART => 17,
CC_C_CHAR::VSTOP => 19,
CC_C_CHAR::VSUSP => 26,
CC_C_CHAR::VEOL => 255,
CC_C_CHAR::VREPRINT => 18,
CC_C_CHAR::VDISCARD => 15,
CC_C_CHAR::VWERASE => 23,
CC_C_CHAR::VLNEXT => 22,
CC_C_CHAR::VEOL2 => 255,
}
}
pub fn as_usize(&self) -> usize {
*self as usize
}
pub fn from_char(item: u8) -> Result<Self> {
if item == Self::VINTR.char() {
return Ok(Self::VINTR);
}
if item == Self::VQUIT.char() {
return Ok(Self::VQUIT);
}
if item == Self::VINTR.char() {
return Ok(Self::VINTR);
}
if item == Self::VERASE.char() {
return Ok(Self::VERASE);
}
if item == Self::VEOF.char() {
return Ok(Self::VEOF);
}
if item == Self::VSTART.char() {
return Ok(Self::VSTART);
}
if item == Self::VSTOP.char() {
return Ok(Self::VSTOP);
}
if item == Self::VSUSP.char() {
return Ok(Self::VSUSP);
}
return_errno_with_message!(Errno::EINVAL, "Not a valid cc_char");
}
}
#[derive(Debug, Clone, Copy, Pod)] #[derive(Debug, Clone, Copy, Pod)]
#[repr(C)] #[repr(C)]
pub struct KernelTermios { pub struct KernelTermios {
pub c_iflags: C_IFLAGS, c_iflags: C_IFLAGS,
pub c_oflags: C_OFLAGS, c_oflags: C_OFLAGS,
pub c_cflags: C_CFLAGS, c_cflags: C_CFLAGS,
pub c_lflags: C_LFLAGS, c_lflags: C_LFLAGS,
pub c_line: CcT, c_line: CcT,
pub c_cc: [CcT; KERNEL_NCCS], c_cc: [CcT; KERNEL_NCCS],
} }
impl KernelTermios { impl KernelTermios {
pub fn default() -> Self { pub fn default() -> Self {
Self { let mut termios = Self {
c_iflags: C_IFLAGS::ICRNL, c_iflags: C_IFLAGS::ICRNL,
c_oflags: C_OFLAGS::empty(), c_oflags: C_OFLAGS::empty(),
c_cflags: C_CFLAGS::B0, c_cflags: C_CFLAGS::B0,
c_lflags: C_LFLAGS::ICANON | C_LFLAGS::ECHO, c_lflags: C_LFLAGS::ICANON | C_LFLAGS::ECHO,
c_line: 0, c_line: 0,
c_cc: [0; KERNEL_NCCS], c_cc: [0; KERNEL_NCCS],
} };
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.char();
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.char();
*termios.get_special_char_mut(CC_C_CHAR::VERASE) = CC_C_CHAR::VERASE.char();
*termios.get_special_char_mut(CC_C_CHAR::VKILL) = CC_C_CHAR::VKILL.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOF) = CC_C_CHAR::VEOF.char();
*termios.get_special_char_mut(CC_C_CHAR::VTIME) = CC_C_CHAR::VTIME.char();
*termios.get_special_char_mut(CC_C_CHAR::VMIN) = CC_C_CHAR::VMIN.char();
*termios.get_special_char_mut(CC_C_CHAR::VSWTC) = CC_C_CHAR::VSWTC.char();
*termios.get_special_char_mut(CC_C_CHAR::VSTART) = CC_C_CHAR::VSTART.char();
*termios.get_special_char_mut(CC_C_CHAR::VSTOP) = CC_C_CHAR::VSTOP.char();
*termios.get_special_char_mut(CC_C_CHAR::VSUSP) = CC_C_CHAR::VSUSP.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL) = CC_C_CHAR::VEOL.char();
*termios.get_special_char_mut(CC_C_CHAR::VREPRINT) = CC_C_CHAR::VREPRINT.char();
*termios.get_special_char_mut(CC_C_CHAR::VDISCARD) = CC_C_CHAR::VDISCARD.char();
*termios.get_special_char_mut(CC_C_CHAR::VWERASE) = CC_C_CHAR::VWERASE.char();
*termios.get_special_char_mut(CC_C_CHAR::VLNEXT) = CC_C_CHAR::VLNEXT.char();
*termios.get_special_char_mut(CC_C_CHAR::VEOL2) = CC_C_CHAR::VEOL2.char();
termios
} }
fn new() -> Self { fn new() -> Self {
@ -147,7 +223,16 @@ impl KernelTermios {
} }
} }
pub fn is_cooked_mode(&self) -> bool { pub fn get_special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT {
&self.c_cc[cc_c_char.as_usize()]
}
pub fn get_special_char_mut(&mut self, cc_c_char: CC_C_CHAR) -> &mut CcT {
&mut self.c_cc[cc_c_char.as_usize()]
}
/// Canonical mode means we will handle input by lines, not by single character
pub fn is_canonical_mode(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ICANON) self.c_lflags.contains(C_LFLAGS::ICANON)
} }
@ -155,4 +240,20 @@ impl KernelTermios {
pub fn contains_icrnl(&self) -> bool { pub fn contains_icrnl(&self) -> bool {
self.c_iflags.contains(C_IFLAGS::ICRNL) self.c_iflags.contains(C_IFLAGS::ICRNL)
} }
pub fn contains_isig(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ISIG)
}
pub fn contain_echo(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ECHO)
}
pub fn contains_echo_ctl(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::ECHOCTL)
}
pub fn contains_iexten(&self) -> bool {
self.c_lflags.contains(C_LFLAGS::IEXTEN)
}
} }

View File

@ -1,11 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
/// This trait is implemented by structs which can handle a user space page fault. /// This trait is implemented by structs which can handle a user space page fault.
/// In current implementation, they are vmars and vmos.
pub trait PageFaultHandler { pub trait PageFaultHandler {
/// Handle a page fault at a specific addr. if not_present is true, the page fault is caused by page not present. /// Handle a page fault at a specific addr. if not_present is true, the page fault is caused by page not present.
/// Otherwise, it's caused by page protection error. /// Otherwise, it's caused by page protection error.
/// if write is true, means the page fault is caused by a write access, /// if write is true, the page fault is caused by a write access,
/// otherwise, the page fault is caused by a read access. /// otherwise, the page fault is caused by a read access.
/// If the page fault can be handled successfully, this function will return Ok(()). /// If the page fault can be handled successfully, this function will return Ok(()).
/// Otherwise, this function will return Err. /// Otherwise, this function will return Err.

View File

@ -29,7 +29,7 @@ fn panic(info: &PanicInfo) -> ! {
fn test_input() { fn test_input() {
jinux_frame::enable_interrupts(); jinux_frame::enable_interrupts();
println!("please input value into console to pass this test"); println!("please input value into console to pass this test");
jinux_std::driver::console::register_console_callback(Arc::new(input_callback)); jinux_std::driver::console::register_serial_input_callback(Arc::new(input_callback));
unsafe { unsafe {
while INPUT_VALUE == 0 { while INPUT_VALUE == 0 {
jinux_frame::hlt(); jinux_frame::hlt();

View File

@ -26,5 +26,5 @@ fn panic(info: &PanicInfo) -> ! {
#[test_case] #[test_case]
fn test_rtc() { fn test_rtc() {
println!("real time:{:?}",jinux_frame::time::get_real_time()); println!("real time:{:?}", jinux_frame::time::get_real_time());
} }