mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
implement canonical line discipline
This commit is contained in:
parent
1e89933a99
commit
6b37f0f956
@ -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
|
||||
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`.
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be0c152c1e47d3109f808cda876c3a90a1ef959007741e126086552e1063eede
|
||||
oid sha256:6db28e1ed8bdac06ac595b2126cefc10ecf16156bf4c1005950f561aedc0531a
|
||||
size 2701792
|
||||
|
@ -15,6 +15,6 @@ pub const PAGE_SIZE_BITS: usize = 0xc;
|
||||
|
||||
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
|
||||
pub const TIMER_FREQ: u64 = 100;
|
||||
|
@ -1,19 +1,19 @@
|
||||
//! Device-related APIs.
|
||||
pub mod framebuffer;
|
||||
|
||||
pub mod console;
|
||||
mod io_port;
|
||||
pub mod pci;
|
||||
pub mod serial;
|
||||
|
||||
pub use self::io_port::IoPort;
|
||||
|
||||
/// first step to init device, call before the memory allocator init
|
||||
pub(crate) fn first_init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
|
||||
framebuffer::init(framebuffer);
|
||||
console::init();
|
||||
serial::init();
|
||||
}
|
||||
|
||||
/// second step to init device, call after the memory allocator init
|
||||
pub(crate) fn second_init() {
|
||||
console::register_console_input_callback(|trap| {});
|
||||
serial::register_serial_input_irq_handler(|trap| {});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
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};
|
||||
|
||||
bitflags::bitflags! {
|
||||
@ -18,8 +20,13 @@ const SERIAL_LINE_CTRL: u16 = SERIAL_DATA + 3;
|
||||
const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4;
|
||||
const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5;
|
||||
lazy_static! {
|
||||
static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> =
|
||||
Cell::new(pic::allocate_irq(4).unwrap());
|
||||
static ref CONSOLE_IRQ_CALLBACK: Cell<IrqAllocateHandle> = {
|
||||
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.
|
||||
@ -43,13 +50,26 @@ pub(crate) fn init() {
|
||||
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
|
||||
F: Fn(&TrapFrame) + Sync + Send + 'static,
|
||||
{
|
||||
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 {
|
||||
LineSts::from_bits_truncate(in8(SERIAL_LINE_STS))
|
||||
}
|
||||
@ -99,13 +119,13 @@ pub fn print(args: fmt::Arguments) {
|
||||
#[macro_export]
|
||||
macro_rules! console_print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::device::console::print(format_args!($fmt $(, $($arg)+)?))
|
||||
$crate::device::serial::print(format_args!($fmt $(, $($arg)+)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! console_println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::device::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
$crate::device::serial::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ pub mod acpi;
|
||||
pub mod apic;
|
||||
pub mod ioapic;
|
||||
pub mod pic;
|
||||
pub mod timer;
|
||||
pub mod rtc;
|
||||
pub mod timer;
|
||||
|
||||
pub use apic::ack;
|
||||
pub use timer::TimerCallback;
|
||||
|
@ -1,52 +1,58 @@
|
||||
use core::sync::atomic::AtomicU8;
|
||||
use core::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
use acpi::{sdt::Signature, fadt::Fadt};
|
||||
use acpi::{fadt::Fadt, sdt::Signature};
|
||||
use lazy_static::lazy_static;
|
||||
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;
|
||||
|
||||
const CMOS_ADDRESS : u16 = 0x70;
|
||||
const CMOS_DATA : u16 = 0x71;
|
||||
pub(crate) static CENTURY_REGISTER : AtomicU8 = AtomicU8::new(0);
|
||||
const CMOS_ADDRESS: u16 = 0x70;
|
||||
const CMOS_DATA: u16 = 0x71;
|
||||
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
lazy_static!{
|
||||
static ref READ_TIME : Mutex<Time> = Mutex::new(Time::default());
|
||||
lazy_static! {
|
||||
static ref READ_TIME: Mutex<Time> = Mutex::new(Time::default());
|
||||
}
|
||||
|
||||
pub fn init(){
|
||||
pub fn init() {
|
||||
let c = ACPI_TABLES.lock();
|
||||
let r_century = unsafe{
|
||||
let a = c.get_sdt::<Fadt>(Signature::FADT).unwrap().expect("not found FACP in ACPI table");
|
||||
let r_century = unsafe {
|
||||
let a = c
|
||||
.get_sdt::<Fadt>(Signature::FADT)
|
||||
.unwrap()
|
||||
.expect("not found FACP in ACPI table");
|
||||
a.century
|
||||
};
|
||||
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);
|
||||
in8(CMOS_DATA)
|
||||
}
|
||||
|
||||
pub fn is_updating() -> bool{
|
||||
out8(CMOS_ADDRESS,0x0A);
|
||||
pub fn is_updating() -> bool {
|
||||
out8(CMOS_ADDRESS, 0x0A);
|
||||
in8(CMOS_DATA) & 0x80 != 0
|
||||
}
|
||||
|
||||
pub fn read()-> Time{
|
||||
pub fn read() -> Time {
|
||||
update_time();
|
||||
READ_TIME.lock().clone()
|
||||
}
|
||||
|
||||
/// read year,month,day and other data
|
||||
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
|
||||
fn update_time(){
|
||||
let mut last_time :Time;
|
||||
fn update_time() {
|
||||
let mut last_time: Time;
|
||||
|
||||
let register_b : u8;
|
||||
let register_b: u8;
|
||||
let mut lock = READ_TIME.lock();
|
||||
|
||||
lock.update_from_rtc();
|
||||
@ -55,18 +61,15 @@ fn update_time(){
|
||||
|
||||
lock.update_from_rtc();
|
||||
|
||||
|
||||
while *lock!=last_time{
|
||||
while *lock != last_time {
|
||||
last_time = lock.clone();
|
||||
|
||||
lock.update_from_rtc();
|
||||
}
|
||||
|
||||
|
||||
register_b = get_cmos(0x0B);
|
||||
|
||||
lock.convert_bcd_to_binary(register_b);
|
||||
lock.convert_12_hour_to_24_hour(register_b);
|
||||
lock.modify_year();
|
||||
}
|
||||
|
||||
|
@ -23,13 +23,13 @@ pub(crate) mod mm;
|
||||
pub mod prelude;
|
||||
pub mod sync;
|
||||
pub mod task;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
pub mod trap;
|
||||
pub mod user;
|
||||
mod util;
|
||||
pub mod vm;
|
||||
pub(crate) mod x86_64_util;
|
||||
pub mod time;
|
||||
|
||||
use core::{mem, panic::PanicInfo};
|
||||
pub use driver::ack as apic_ack;
|
||||
@ -45,7 +45,7 @@ use bootloader::{
|
||||
boot_info::{FrameBuffer, MemoryRegionKind},
|
||||
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::page_table::translate_not_offset_virtual_address;
|
||||
pub use trap::{allocate_irq, IrqAllocateHandle, TrapFrame};
|
||||
|
@ -23,7 +23,7 @@ pub fn log_print(args: Arguments) {
|
||||
#[cfg(feature = "serial_print")]
|
||||
#[doc(hidden)]
|
||||
pub fn log_print(args: Arguments) {
|
||||
crate::device::console::print(args);
|
||||
crate::device::serial::print(args);
|
||||
}
|
||||
|
||||
/// This macro should not be directly called.
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
||||
#[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,
|
||||
pub year: u16,
|
||||
pub month: u8,
|
||||
@ -14,31 +12,32 @@ pub struct Time{
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
impl Time{
|
||||
pub(crate) fn update_from_rtc(&mut self){
|
||||
while is_updating(){}
|
||||
impl Time {
|
||||
pub(crate) fn update_from_rtc(&mut self) {
|
||||
while is_updating() {}
|
||||
self.second = get_cmos(0x00);
|
||||
self.minute = get_cmos(0x02);
|
||||
self.hour = get_cmos(0x04);
|
||||
self.day = get_cmos(0x07);
|
||||
self.month = get_cmos(0x08);
|
||||
self.year = get_cmos(0x09) as u16;
|
||||
|
||||
|
||||
let century_register = CENTURY_REGISTER.load(Relaxed);
|
||||
|
||||
if century_register !=0{
|
||||
|
||||
if century_register != 0 {
|
||||
self.century = get_cmos(century_register);
|
||||
}
|
||||
}
|
||||
|
||||
/// convert BCD to binary values
|
||||
/// 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){
|
||||
if register_b & 0x04 == 0{
|
||||
pub(crate) fn convert_bcd_to_binary(&mut self, register_b: u8) {
|
||||
if register_b & 0x04 == 0 {
|
||||
let century_register = CENTURY_REGISTER.load(Relaxed);
|
||||
self.second = (self.second & 0x0F) + ((self.second / 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.month = (self.month & 0x0F) + ((self.month / 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
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
if century_register !=0{
|
||||
if century_register != 0 {
|
||||
self.year += self.century as u16 * 100;
|
||||
}else{
|
||||
} else {
|
||||
panic!("century register not exists");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// get real time
|
||||
pub fn get_real_time() -> Time{
|
||||
pub fn get_real_time() -> Time {
|
||||
read()
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ pub(crate) fn init() {
|
||||
model_specific::SFMask::write(
|
||||
RFlags::TRAP_FLAG
|
||||
| RFlags::DIRECTION_FLAG
|
||||
| RFlags::INTERRUPT_FLAG
|
||||
// | RFlags::INTERRUPT_FLAG
|
||||
| RFlags::IOPL_LOW
|
||||
| RFlags::IOPL_HIGH
|
||||
| RFlags::NESTED_TASK
|
||||
|
@ -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))?;
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
pub mod console;
|
||||
pub mod pci;
|
||||
pub mod tty;
|
||||
|
||||
pub fn init() {
|
||||
pci::init();
|
||||
console::init();
|
||||
tty::init();
|
||||
}
|
||||
|
80
src/services/libs/jinux-std/src/driver/tty.rs
Normal file
80
src/services/libs/jinux-std/src/driver/tty.rs
Normal 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));
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use crate::tty::get_console;
|
||||
use crate::tty::get_n_tty;
|
||||
use core::any::Any;
|
||||
|
||||
use super::events::IoEvents;
|
||||
@ -21,7 +21,7 @@ pub trait File: Send + Sync + Any {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
// FIXME: only a work around
|
||||
let tty = get_console();
|
||||
let tty = get_n_tty();
|
||||
tty.ioctl(cmd, arg)
|
||||
}
|
||||
_ => panic!("Ioctl unsupported"),
|
||||
|
@ -67,4 +67,14 @@ impl Stat {
|
||||
stat.st_blksize = 4096;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::events::IoEvents;
|
||||
use crate::prelude::*;
|
||||
use crate::tty::{get_console, Tty};
|
||||
use crate::tty::{get_n_tty, Tty};
|
||||
|
||||
use super::file::{File, FileDescripter};
|
||||
|
||||
@ -10,23 +10,19 @@ pub const FD_STDERR: FileDescripter = 2;
|
||||
|
||||
pub struct Stdin {
|
||||
console: Option<Arc<Tty>>,
|
||||
bind_to_console: bool,
|
||||
}
|
||||
|
||||
pub struct Stdout {
|
||||
console: Option<Arc<Tty>>,
|
||||
bind_to_console: bool,
|
||||
}
|
||||
|
||||
pub struct Stderr {
|
||||
console: Option<Arc<Tty>>,
|
||||
bind_to_console: bool,
|
||||
}
|
||||
|
||||
impl File for Stdin {
|
||||
fn poll(&self) -> IoEvents {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.poll()
|
||||
} else {
|
||||
todo!()
|
||||
@ -34,8 +30,7 @@ impl File for Stdin {
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.read(buf)
|
||||
} else {
|
||||
todo!()
|
||||
@ -43,8 +38,7 @@ impl File for Stdin {
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.ioctl(cmd, arg)
|
||||
} else {
|
||||
todo!()
|
||||
@ -53,8 +47,7 @@ impl File for Stdin {
|
||||
}
|
||||
impl File for Stdout {
|
||||
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.ioctl(cmd, arg)
|
||||
} else {
|
||||
todo!()
|
||||
@ -62,8 +55,7 @@ impl File for Stdout {
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.write(buf)
|
||||
} else {
|
||||
todo!()
|
||||
@ -73,8 +65,7 @@ impl File for Stdout {
|
||||
|
||||
impl File for Stderr {
|
||||
fn ioctl(&self, cmd: super::ioctl::IoctlCmd, arg: usize) -> Result<i32> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.ioctl(cmd, arg)
|
||||
} else {
|
||||
todo!()
|
||||
@ -82,8 +73,7 @@ impl File for Stderr {
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if self.bind_to_console {
|
||||
let console = self.console.as_ref().unwrap();
|
||||
if let Some(console) = self.console.as_ref() {
|
||||
console.write(buf)
|
||||
} else {
|
||||
todo!()
|
||||
@ -95,10 +85,9 @@ impl Stdin {
|
||||
/// FIXME: console should be file under devfs.
|
||||
/// reimplement the function when devfs is enabled.
|
||||
pub fn new_with_default_console() -> Self {
|
||||
let console = get_console();
|
||||
let console = get_n_tty();
|
||||
Self {
|
||||
console: Some(console.clone()),
|
||||
bind_to_console: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,10 +96,9 @@ impl Stdout {
|
||||
/// FIXME: console should be file under devfs.
|
||||
/// reimplement the function when devfs is enabled.
|
||||
pub fn new_with_default_console() -> Self {
|
||||
let console = get_console();
|
||||
let console = get_n_tty();
|
||||
Self {
|
||||
console: Some(console.clone()),
|
||||
bind_to_console: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,10 +107,9 @@ impl Stderr {
|
||||
/// FIXME: console should be file under devfs.
|
||||
/// reimplement the function when devfs is enabled.
|
||||
pub fn new_with_default_console() -> Self {
|
||||
let console = get_console();
|
||||
let console = get_n_tty();
|
||||
Self {
|
||||
console: Some(console.clone()),
|
||||
bind_to_console: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use crate::prelude::*;
|
||||
use crate::rights::Full;
|
||||
use crate::thread::kernel_thread::KernelThreadExt;
|
||||
use crate::thread::{thread_table, Thread};
|
||||
use crate::tty::get_console;
|
||||
use crate::tty::get_n_tty;
|
||||
use crate::vm::vmar::Vmar;
|
||||
use jinux_frame::sync::WaitQueue;
|
||||
use jinux_frame::task::Task;
|
||||
@ -149,7 +149,9 @@ impl Process {
|
||||
let process = Process::create_user_process(filename, elf_file_content, argv, envp);
|
||||
// FIXME: How to determine the fg process group?
|
||||
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
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
||||
pub struct ProcessGroup {
|
||||
@ -43,6 +43,10 @@ impl ProcessGroup {
|
||||
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.
|
||||
/// If this group contains no processes now, the group itself will be deleted from global table.
|
||||
pub fn remove_process(&self, pid: Pid) {
|
||||
@ -68,4 +72,14 @@ impl ProcessGroup {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_tid_address::sys_set_tid_address;
|
||||
use crate::syscall::setpgid::sys_setpgid;
|
||||
use crate::syscall::stat::sys_stat;
|
||||
use crate::syscall::tgkill::sys_tgkill;
|
||||
use crate::syscall::uname::sys_uname;
|
||||
use crate::syscall::wait4::sys_wait4;
|
||||
@ -96,6 +97,7 @@ mod sched_yield;
|
||||
mod set_robust_list;
|
||||
mod set_tid_address;
|
||||
mod setpgid;
|
||||
mod stat;
|
||||
mod tgkill;
|
||||
mod uname;
|
||||
mod wait4;
|
||||
@ -137,6 +139,7 @@ define_syscall_nums!(
|
||||
SYS_READ = 0,
|
||||
SYS_WRITE = 1,
|
||||
SYS_CLOSE = 3,
|
||||
SYS_STAT = 4,
|
||||
SYS_FSTAT = 5,
|
||||
SYS_LSTAT = 6,
|
||||
SYS_POLL = 7,
|
||||
@ -244,6 +247,7 @@ pub fn syscall_dispatch(
|
||||
SYS_READ => syscall_handler!(3, sys_read, args),
|
||||
SYS_WRITE => syscall_handler!(3, sys_write, 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_LSTAT => syscall_handler!(2, sys_lstat, 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_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::fs::file_handle::FileHandle;
|
||||
use crate::log_syscall_entry;
|
||||
use crate::prelude::*;
|
||||
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 super::SyscallReturn;
|
||||
@ -38,11 +38,16 @@ pub fn sys_openat(
|
||||
}
|
||||
|
||||
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")? {
|
||||
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 mut file_table = current.file_table().lock();
|
||||
let fd = file_table.insert(tty_file);
|
||||
@ -50,3 +55,13 @@ pub fn sys_openat(
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,11 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
|
||||
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) {
|
||||
new_process_group.add_process(current.clone());
|
||||
current.set_process_group(Arc::downgrade(&new_process_group));
|
||||
|
16
src/services/libs/jinux-std/src/syscall/stat.rs
Normal file
16
src/services/libs/jinux-std/src/syscall/stat.rs
Normal 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");
|
||||
}
|
@ -25,6 +25,7 @@ pub fn sys_write(
|
||||
let file = file_table.get_file(fd)?;
|
||||
let mut buffer = vec![0u8; user_buf_len];
|
||||
read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?;
|
||||
debug!("write content = {:?}", buffer);
|
||||
let write_len = file.write(&buffer)?;
|
||||
Ok(SyscallReturn::Return(write_len as _))
|
||||
}
|
||||
|
@ -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 super::termio::KernelTermios;
|
||||
use super::termio::{KernelTermios, CC_C_CHAR};
|
||||
|
||||
// This implementation refers the implementation of linux
|
||||
// https://elixir.bootlin.com/linux/latest/source/include/linux/tty_ldisc.h
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LineDiscipline {
|
||||
/// The write buffer
|
||||
buffer: ConstGenericRingBuffer<u8, BUFFER_CAPACITY>,
|
||||
/// current line
|
||||
current_line: CurrentLine,
|
||||
/// The read buffer
|
||||
read_buffer: Mutex<ConstGenericRingBuffer<u8, BUFFER_CAPACITY>>,
|
||||
/// The foreground process group
|
||||
foreground: Option<Pgid>,
|
||||
/// termios
|
||||
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 {
|
||||
/// create a new line discipline
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffer: ConstGenericRingBuffer::new(),
|
||||
current_line: CurrentLine::new(),
|
||||
read_buffer: Mutex::new(ConstGenericRingBuffer::new()),
|
||||
foreground: None,
|
||||
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) {
|
||||
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 item == b'\r' {
|
||||
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.
|
||||
pub fn read(&mut self, dst: &mut [u8]) -> Result<usize> {
|
||||
let len = self.buffer.len();
|
||||
let read_len = len.min(dst.len());
|
||||
for i in 0..read_len {
|
||||
if let Some(content) = self.buffer.dequeue() {
|
||||
dst[i] = content;
|
||||
let vmin = *self.termios.get_special_char(CC_C_CHAR::VMIN);
|
||||
let vtime = *self.termios.get_special_char(CC_C_CHAR::VTIME);
|
||||
let read_len: usize = self.read_wait_queue.wait_until(|| {
|
||||
// if current process does not belong to foreground process group,
|
||||
// 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 {
|
||||
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
|
||||
@ -60,9 +258,27 @@ impl LineDiscipline {
|
||||
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
|
||||
pub fn set_fg(&mut self, fg_pgid: 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
|
||||
@ -72,7 +288,7 @@ impl LineDiscipline {
|
||||
|
||||
/// whether there is buffered data
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.buffer.len() == 0
|
||||
self.read_buffer.lock().len() == 0
|
||||
}
|
||||
|
||||
pub fn get_termios(&self) -> &KernelTermios {
|
||||
@ -83,3 +299,27 @@ impl LineDiscipline {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
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::ioctl::IoctlCmd;
|
||||
use crate::process::{process_table, Pgid};
|
||||
use crate::util::{read_val_from_user, write_val_to_user};
|
||||
@ -22,6 +22,8 @@ pub struct Tty {
|
||||
name: CString,
|
||||
/// line discipline
|
||||
ldisc: Mutex<LineDiscipline>,
|
||||
/// driver
|
||||
driver: Mutex<Weak<TtyDriver>>,
|
||||
}
|
||||
|
||||
impl Tty {
|
||||
@ -29,6 +31,7 @@ impl Tty {
|
||||
Tty {
|
||||
name,
|
||||
ldisc: Mutex::new(LineDiscipline::new()),
|
||||
driver: Mutex::new(Weak::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,9 +40,13 @@ impl Tty {
|
||||
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.
|
||||
/// 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();
|
||||
if let Some(fg_pgid) = ldisc.get_fg() {
|
||||
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 {
|
||||
@ -64,13 +76,7 @@ impl File for Tty {
|
||||
}
|
||||
|
||||
fn poll(&self) -> IoEvents {
|
||||
if !self.ldisc.lock().is_empty() {
|
||||
return IoEvents::POLLIN;
|
||||
}
|
||||
// receive keyboard input
|
||||
let byte = receive_console_char();
|
||||
self.ldisc.lock().push_char(byte);
|
||||
return IoEvents::POLLIN;
|
||||
self.ldisc.lock().poll()
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
@ -121,6 +127,6 @@ impl File for Tty {
|
||||
}
|
||||
|
||||
/// FIXME: should we maintain a static console?
|
||||
pub fn get_console() -> &'static Arc<Tty> {
|
||||
pub fn get_n_tty() -> &'static Arc<Tty> {
|
||||
&N_TTY
|
||||
}
|
||||
|
@ -113,27 +113,103 @@ pub enum CC_C_CHAR {
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct KernelTermios {
|
||||
pub c_iflags: C_IFLAGS,
|
||||
pub c_oflags: C_OFLAGS,
|
||||
pub c_cflags: C_CFLAGS,
|
||||
pub c_lflags: C_LFLAGS,
|
||||
pub c_line: CcT,
|
||||
pub c_cc: [CcT; KERNEL_NCCS],
|
||||
c_iflags: C_IFLAGS,
|
||||
c_oflags: C_OFLAGS,
|
||||
c_cflags: C_CFLAGS,
|
||||
c_lflags: C_LFLAGS,
|
||||
c_line: CcT,
|
||||
c_cc: [CcT; KERNEL_NCCS],
|
||||
}
|
||||
|
||||
impl KernelTermios {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
let mut termios = Self {
|
||||
c_iflags: C_IFLAGS::ICRNL,
|
||||
c_oflags: C_OFLAGS::empty(),
|
||||
c_cflags: C_CFLAGS::B0,
|
||||
c_lflags: C_LFLAGS::ICANON | C_LFLAGS::ECHO,
|
||||
c_line: 0,
|
||||
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 {
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -155,4 +240,20 @@ impl KernelTermios {
|
||||
pub fn contains_icrnl(&self) -> bool {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// 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 {
|
||||
/// 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.
|
||||
/// 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.
|
||||
/// If the page fault can be handled successfully, this function will return Ok(()).
|
||||
/// Otherwise, this function will return Err.
|
||||
|
@ -29,7 +29,7 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
fn test_input() {
|
||||
jinux_frame::enable_interrupts();
|
||||
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 {
|
||||
while INPUT_VALUE == 0 {
|
||||
jinux_frame::hlt();
|
||||
|
@ -26,5 +26,5 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
|
||||
#[test_case]
|
||||
fn test_rtc() {
|
||||
println!("real time:{:?}",jinux_frame::time::get_real_time());
|
||||
println!("real time:{:?}", jinux_frame::time::get_real_time());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user