diff --git a/README.md b/README.md
index 35f5a35..38be12e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Going to be the next generation teaching operating system.
Supported architectures: x86_64, RISCV32/64, AArch64, MIPS32
-Tested boards: QEMU, HiFive Unleashed, x86_64 PC (i5/i7), Raspberry Pi 3B+, Kendryte K210 and FPGA running Rocket Chip
+Tested boards: QEMU, HiFive Unleashed, x86_64 PC (i5/i7), Raspberry Pi 3B+, Kendryte K210 and FPGA running Rocket Chip / [TrivialMIPS](https://github.com/Harry-Chen/TrivialMIPS)

@@ -50,9 +50,9 @@ $ make run arch=x86_64 mode=release pci_passthru=0000:00:00.1 # for ixgbe real n
| Module | Maintainer |
|--------|-----------------------|
| x86_64 | @wangrunji0408 |
-| RISCV | @jiegec |
+| RISC-V | @jiegec |
| ARM (Raspi3) | @equation314 |
-| MIPS | @HarryChen @miskcoo |
+| MIPS | @Harry_Chen @miskcoo |
| Memory, Process, File System | @wangrunji0408 |
| Network with drivers | @jiegec |
| GUI | @equation314 |
diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock
index 15dc395..a674b88 100644
--- a/kernel/Cargo.lock
+++ b/kernel/Cargo.lock
@@ -411,7 +411,7 @@ dependencies = [
[[package]]
name = "rcore-thread"
version = "0.1.0"
-source = "git+https://github.com/rcore-os/rcore-thread#fd972c7e3aa2b7618f625f143655c16adfd2ca78"
+source = "git+https://github.com/rcore-os/rcore-thread#77e8e0778154734ee59525e043caab6339e14d75"
dependencies = [
"deque 0.3.2 (git+https://github.com/rcore-os/deque.git?branch=no_std)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/kernel/src/arch/mipsel/paging.rs b/kernel/src/arch/mipsel/paging.rs
index af0a782..b949dbc 100644
--- a/kernel/src/arch/mipsel/paging.rs
+++ b/kernel/src/arch/mipsel/paging.rs
@@ -23,7 +23,7 @@ impl PageTable for ActivePageTable {
let frame = Frame::of_addr(PhysAddr::new(target));
// we may need frame allocator to alloc frame for new page table(first/second)
self.get_table()
- .map_to(page, frame, flags, &mut FrameAllocatorForRiscv)
+ .map_to(page, frame, flags, &mut FrameAllocatorForMips)
.unwrap()
.flush();
self.get_entry(addr).expect("fail to get entry")
@@ -254,15 +254,15 @@ impl Drop for InactivePageTable0 {
}
}
-struct FrameAllocatorForRiscv;
+struct FrameAllocatorForMips;
-impl FrameAllocator for FrameAllocatorForRiscv {
+impl FrameAllocator for FrameAllocatorForMips {
fn alloc(&mut self) -> Option {
alloc_frame().map(|addr| Frame::of_addr(PhysAddr::new(addr)))
}
}
-impl FrameDeallocator for FrameAllocatorForRiscv {
+impl FrameDeallocator for FrameAllocatorForMips {
fn dealloc(&mut self, frame: Frame) {
dealloc_frame(frame.start_address().as_usize());
}
diff --git a/kernel/src/arch/riscv32/board/rocket_chip/device.dts b/kernel/src/arch/riscv32/board/rocket_chip/device.dts
index 42cf330..048a047 100644
--- a/kernel/src/arch/riscv32/board/rocket_chip/device.dts
+++ b/kernel/src/arch/riscv32/board/rocket_chip/device.dts
@@ -100,6 +100,13 @@
compatible = "xlnx,xps-intc-1.00.a";
reg = <0x61200000 0x1000>;
interrupt-parent = <&L10>;
+ interrupts = <0>;
+ };
+
+ router: router@64A00000 {
+ compatible = "rcore,router";
+ reg = <0x64A00000 0x1000>;
+ interrupt-parent = <&L10>;
interrupts = <1>;
};
};
diff --git a/kernel/src/arch/riscv32/board/rocket_chip/mod.rs b/kernel/src/arch/riscv32/board/rocket_chip/mod.rs
index cb14a51..8a96256 100644
--- a/kernel/src/arch/riscv32/board/rocket_chip/mod.rs
+++ b/kernel/src/arch/riscv32/board/rocket_chip/mod.rs
@@ -1,19 +1,22 @@
use super::consts::KERNEL_OFFSET;
+use crate::memory::active_table;
+use rcore_memory::paging::PageTable;
/// Mask all external interrupt except serial.
pub unsafe fn init_external_interrupt() {
const HART0_S_MODE_INTERRUPT_ENABLES: *mut u64 = (KERNEL_OFFSET + 0x0C00_2080) as *mut u64;
+ // enable all external interrupts
HART0_S_MODE_INTERRUPT_ENABLES.write_volatile(0xf);
// mask interrupts first
- const AXI_INTC_IER: *mut u32 = (KERNEL_OFFSET + 0x1900_0008) as *mut u32;
+ const AXI_INTC_IER: *mut u32 = (KERNEL_OFFSET + 0x1810_0008) as *mut u32;
AXI_INTC_IER.write_volatile(0x0);
// acknowledge all interrupts
- const AXI_INTC_IAR: *mut u32 = (KERNEL_OFFSET + 0x1900_000C) as *mut u32;
+ const AXI_INTC_IAR: *mut u32 = (KERNEL_OFFSET + 0x1810_000C) as *mut u32;
AXI_INTC_IAR.write_volatile(0xffffffff);
- const AXI_INTC_MER: *mut u32 = (KERNEL_OFFSET + 0x1900_001C) as *mut u32;
+ const AXI_INTC_MER: *mut u32 = (KERNEL_OFFSET + 0x1810_001C) as *mut u32;
// Hardware Interrupt enable | Enable irq output
AXI_INTC_MER.write_volatile(0b11);
@@ -32,7 +35,7 @@ pub unsafe fn handle_external_interrupt() {
HART0_S_MODE_INTERRUPT_CLAIM_COMPLETE.write_volatile(source);
// acknowledge all interrupts
- const AXI_INTC_IAR: *mut u32 = (KERNEL_OFFSET + 0x1900_000C) as *mut u32;
+ const AXI_INTC_IAR: *mut u32 = (KERNEL_OFFSET + 0x1810_000C) as *mut u32;
AXI_INTC_IAR.write_volatile(0xffffffff);
}
diff --git a/kernel/src/arch/riscv32/memory.rs b/kernel/src/arch/riscv32/memory.rs
index 304e9f6..b5f3161 100644
--- a/kernel/src/arch/riscv32/memory.rs
+++ b/kernel/src/arch/riscv32/memory.rs
@@ -154,12 +154,21 @@ fn remap_the_kernel(dtb: usize) {
// map AXI INTC for Rocket Chip
#[cfg(feature = "board_rocket_chip")]
ms.push(
- KERNEL_OFFSET + 0x19000000,
- KERNEL_OFFSET + 0x19000000 + PAGE_SIZE,
+ KERNEL_OFFSET + 0x18100000,
+ KERNEL_OFFSET + 0x18100000 + PAGE_SIZE,
MemoryAttr::default(),
- Linear::new(-(KERNEL_OFFSET as isize + 0x19000000 - 0x61200000)),
+ Linear::new(-(KERNEL_OFFSET as isize + 0x18100000 - 0x61200000)),
"axi_intc",
);
+ // map AXI4-Stream Data FIFO for Rocket Chip
+ #[cfg(feature = "board_rocket_chip")]
+ ms.push(
+ KERNEL_OFFSET + 0x18200000,
+ KERNEL_OFFSET + 0x18200000 + PAGE_SIZE,
+ MemoryAttr::default(),
+ Linear::new(-(KERNEL_OFFSET as isize + 0x18200000 - 0x64A00000)),
+ "router",
+ );
unsafe {
ms.activate();
}
diff --git a/kernel/src/drivers/device_tree.rs b/kernel/src/drivers/device_tree.rs
index 656b7e2..8b74fc1 100644
--- a/kernel/src/drivers/device_tree.rs
+++ b/kernel/src/drivers/device_tree.rs
@@ -4,6 +4,7 @@ use core::slice;
use device_tree::{DeviceTree, Node};
use super::bus::virtio_mmio::virtio_probe;
+use super::net::router::router_init;
use super::CMDLINE;
const DEVICE_TREE_MAGIC: u32 = 0xd00dfeed;
@@ -14,6 +15,9 @@ fn walk_dt_node(dt: &Node) {
if compatible == "virtio,mmio" {
virtio_probe(dt);
}
+ if compatible == "rcore,router" {
+ router_init();
+ }
// TODO: initial other devices (16650, etc.)
}
if let Ok(bootargs) = dt.prop_str("bootargs") {
diff --git a/kernel/src/drivers/net/mod.rs b/kernel/src/drivers/net/mod.rs
index 0019043..87d321b 100644
--- a/kernel/src/drivers/net/mod.rs
+++ b/kernel/src/drivers/net/mod.rs
@@ -1,3 +1,4 @@
pub mod e1000;
pub mod ixgbe;
+pub mod router;
pub mod virtio_net;
diff --git a/kernel/src/drivers/net/router.rs b/kernel/src/drivers/net/router.rs
new file mode 100644
index 0000000..8c1e929
--- /dev/null
+++ b/kernel/src/drivers/net/router.rs
@@ -0,0 +1,229 @@
+//! rCore Router Driver
+
+use alloc::collections::BTreeMap;
+use alloc::string::String;
+use alloc::sync::Arc;
+use alloc::vec::Vec;
+
+use smoltcp::iface::*;
+use smoltcp::phy::{self, DeviceCapabilities};
+use smoltcp::time::Instant;
+use smoltcp::wire::*;
+use smoltcp::Result;
+
+use rcore_memory::PAGE_SIZE;
+
+use crate::drivers::provider::Provider;
+use crate::net::SOCKETS;
+use crate::sync::SpinNoIrqLock as Mutex;
+
+use super::super::{DeviceType, Driver, DRIVERS, NET_DRIVERS, SOCKET_ACTIVITY};
+use crate::consts::{KERNEL_OFFSET, MEMORY_OFFSET};
+
+const AXI_STREAM_FIFO_ISR: *mut u32 = (KERNEL_OFFSET + 0x1820_0000) as *mut u32;
+const AXI_STREAM_FIFO_IER: *mut u32 = (KERNEL_OFFSET + 0x1820_0004) as *mut u32;
+const AXI_STREAM_FIFO_TDFR: *mut u32 = (KERNEL_OFFSET + 0x1820_0008) as *mut u32;
+const AXI_STREAM_FIFO_TDFD: *mut u32 = (KERNEL_OFFSET + 0x1820_0010) as *mut u32;
+const AXI_STREAM_FIFO_TLR: *mut u32 = (KERNEL_OFFSET + 0x1820_0014) as *mut u32;
+const AXI_STREAM_FIFO_RDFR: *mut u32 = (KERNEL_OFFSET + 0x1820_0018) as *mut u32;
+const AXI_STREAM_FIFO_RDFO: *mut u32 = (KERNEL_OFFSET + 0x1820_001C) as *mut u32;
+const AXI_STREAM_FIFO_RDFD: *mut u32 = (KERNEL_OFFSET + 0x1820_0020) as *mut u32;
+const AXI_STREAM_FIFO_RLR: *mut u32 = (KERNEL_OFFSET + 0x1820_0024) as *mut u32;
+const AXI_STREAM_FIFO_TDR: *mut u32 = (KERNEL_OFFSET + 0x1820_002C) as *mut u32;
+const AXI_STREAM_FIFO_RDR: *mut u32 = (KERNEL_OFFSET + 0x1820_0030) as *mut u32;
+
+pub struct Router {
+ buffer: Vec>,
+}
+
+impl Router {
+ fn transmit_available(&self) -> bool {
+ true
+ }
+
+ fn receive_available(&self) -> bool {
+ self.buffer.len() > 0
+ }
+}
+
+#[derive(Clone)]
+pub struct RouterDriver(Arc>);
+
+pub struct RouterRxToken(RouterDriver);
+pub struct RouterTxToken(RouterDriver);
+
+impl<'a> phy::Device<'a> for RouterDriver {
+ type RxToken = RouterRxToken;
+ type TxToken = RouterTxToken;
+
+ fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
+ let driver = self.0.lock();
+ if driver.transmit_available() && driver.receive_available() {
+ // potential racing
+ Some((RouterRxToken(self.clone()), RouterTxToken(self.clone())))
+ } else {
+ None
+ }
+ }
+
+ fn transmit(&'a mut self) -> Option {
+ let driver = self.0.lock();
+ if driver.transmit_available() {
+ Some(RouterTxToken(self.clone()))
+ } else {
+ None
+ }
+ }
+
+ fn capabilities(&self) -> DeviceCapabilities {
+ let mut caps = DeviceCapabilities::default();
+ caps.max_transmission_unit = 1536;
+ caps.max_burst_size = Some(1);
+ caps
+ }
+}
+
+impl phy::RxToken for RouterRxToken {
+ fn consume(self, _timestamp: Instant, f: F) -> Result
+ where
+ F: FnOnce(&[u8]) -> Result,
+ {
+ let mut router = (self.0).0.lock();
+ let buffer = router.buffer.pop().unwrap();
+ f(&buffer)
+ }
+}
+
+impl phy::TxToken for RouterTxToken {
+ fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result
+ where
+ F: FnOnce(&mut [u8]) -> Result,
+ {
+ let mut buffer = vec![0; len];
+ let res = f(&mut buffer);
+ debug!("out buf {}", len);
+
+ unsafe {
+ AXI_STREAM_FIFO_TDR.write_volatile(2);
+ for byte in buffer {
+ AXI_STREAM_FIFO_TDFD.write_volatile(byte as u32);
+ }
+ AXI_STREAM_FIFO_TLR.write((len * 4) as u32);
+ }
+ res
+ }
+}
+
+pub struct RouterInterface {
+ iface: Mutex>,
+ driver: RouterDriver,
+}
+
+impl Driver for RouterInterface {
+ fn try_handle_interrupt(&self, _irq: Option) -> bool {
+ let mut driver = self.driver.0.lock();
+
+ let isr = unsafe { AXI_STREAM_FIFO_ISR.read_volatile() };
+
+ if isr > 0 {
+ debug!("handle router interrupt {:b}", isr);
+ unsafe {
+ AXI_STREAM_FIFO_ISR.write(isr);
+ let rdfo = AXI_STREAM_FIFO_RDFO.read_volatile();
+ if rdfo > 0 {
+ let mut buffer = Vec::new();
+ let rlr = AXI_STREAM_FIFO_RLR.read_volatile();
+ let rdr = AXI_STREAM_FIFO_RDR.read_volatile();
+ for i in 0..rdfo {
+ buffer.push(AXI_STREAM_FIFO_RDFD.read_volatile() as u8);
+ }
+ debug!("got packet of length {}", rdfo);
+ driver.buffer.push(buffer);
+ }
+ drop(driver);
+
+ let timestamp = Instant::from_millis(crate::trap::uptime_msec() as i64);
+ let mut sockets = SOCKETS.lock();
+ match self.iface.lock().poll(&mut sockets, timestamp) {
+ Ok(_) => {
+ SOCKET_ACTIVITY.notify_all();
+ }
+ Err(err) => {
+ debug!("poll got err {}", err);
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ fn device_type(&self) -> DeviceType {
+ DeviceType::Net
+ }
+
+ fn get_id(&self) -> String {
+ format!("router")
+ }
+
+ fn get_mac(&self) -> EthernetAddress {
+ unimplemented!()
+ }
+
+ fn get_ifname(&self) -> String {
+ format!("router")
+ }
+
+ fn ipv4_address(&self) -> Option {
+ unimplemented!()
+ }
+
+ fn poll(&self) {
+ unimplemented!()
+ }
+}
+
+pub fn router_init() -> Arc {
+ unsafe {
+ // reset tx fifo
+ AXI_STREAM_FIFO_TDFR.write_volatile(0xA5);
+ // reset rx fifo
+ AXI_STREAM_FIFO_RDFR.write_volatile(0xA5);
+ }
+
+ let ethernet_addr = EthernetAddress::from_bytes(&[2, 2, 3, 3, 0, 0]);
+
+ let net_driver = RouterDriver(Arc::new(Mutex::new(Router { buffer: Vec::new() })));
+
+ let ip_addrs = [
+ IpCidr::new(IpAddress::v4(10, 0, 0, 1), 24),
+ IpCidr::new(IpAddress::v4(10, 0, 1, 1), 24),
+ ];
+ let neighbor_cache = NeighborCache::new(BTreeMap::new());
+ let routes = Routes::new(BTreeMap::new());
+ let iface = EthernetInterfaceBuilder::new(net_driver.clone())
+ .ethernet_addr(ethernet_addr)
+ .ip_addrs(ip_addrs)
+ .neighbor_cache(neighbor_cache)
+ .routes(routes)
+ .finalize();
+
+ info!("router interface up");
+
+ let router_iface = RouterInterface {
+ iface: Mutex::new(iface),
+ driver: net_driver,
+ };
+
+ let driver = Arc::new(router_iface);
+ DRIVERS.write().push(driver.clone());
+ NET_DRIVERS.write().push(driver.clone());
+
+ const AXI_STREAM_FIFO_IER: *mut u32 = (KERNEL_OFFSET + 0x1820_0004) as *mut u32;
+ // Enable Receive Complete Interrupt
+ unsafe {
+ AXI_STREAM_FIFO_IER.write_volatile(1 << 26);
+ }
+
+ driver
+}
diff --git a/kernel/src/fs/pipe.rs b/kernel/src/fs/pipe.rs
index 6059e1a..59fa2fa 100644
--- a/kernel/src/fs/pipe.rs
+++ b/kernel/src/fs/pipe.rs
@@ -45,13 +45,25 @@ impl Pipe {
)
}
- pub fn can_read(&self) -> bool {
+ fn can_read(&self) -> bool {
if let PipeEnd::Read = self.direction {
- self.data.lock().buf.len() > 0
+ self.data.lock().buf.len() > 0 || self.is_broken()
} else {
false
}
}
+
+ fn can_write(&self) -> bool {
+ if let PipeEnd::Write = self.direction {
+ !self.is_broken()
+ } else {
+ false
+ }
+ }
+
+ fn is_broken(&self) -> bool {
+ Arc::strong_count(&self.data) < 2
+ }
}
// TODO: better way to provide default impl?
@@ -105,39 +117,11 @@ impl INode for Pipe {
}
fn poll(&self) -> Result {
- let data = self.data.lock();
- match self.direction {
- PipeEnd::Read => {
- if data.buf.len() > 0 {
- Ok(PollStatus {
- read: true,
- write: false,
- error: false,
- })
- } else {
- Ok(PollStatus {
- read: false,
- write: false,
- error: false,
- })
- }
- }
- PipeEnd::Write => {
- if data.buf.len() > 0 {
- Ok(PollStatus {
- read: false,
- write: true,
- error: false,
- })
- } else {
- Ok(PollStatus {
- read: false,
- write: false,
- error: false,
- })
- }
- }
- }
+ Ok(PollStatus {
+ read: self.can_read(),
+ write: self.can_write(),
+ error: false,
+ })
}
impl_inode!();
}
diff --git a/kernel/src/logging.rs b/kernel/src/logging.rs
index 838abe5..5cf1ba2 100644
--- a/kernel/src/logging.rs
+++ b/kernel/src/logging.rs
@@ -3,6 +3,7 @@ use core::fmt;
use lazy_static::lazy_static;
use log::{self, Level, LevelFilter, Log, Metadata, Record};
+use crate::processor;
use crate::sync::SpinNoIrqLock as Mutex;
use crate::util::color::ConsoleColor;
@@ -63,12 +64,17 @@ impl Log for SimpleLogger {
true
}
fn log(&self, record: &Record) {
- static DISABLED_TARGET: &[&str] = &[];
- if self.enabled(record.metadata()) && !DISABLED_TARGET.contains(&record.target()) {
- // let target = record.target();
- // let begin = target.as_bytes().iter().rposition(|&c| c == b':').map(|i| i + 1).unwrap_or(0);
+ if !self.enabled(record.metadata()) {
+ return;
+ }
+ if let Some(tid) = processor().tid_option() {
+ print_in_color(
+ format_args!("[{:>5}][{}] {}\n", record.level(), tid, record.args()),
+ ConsoleColor::from(record.level()),
+ );
+ } else {
print_in_color(
- format_args!("[{:>5}] {}\n", record.level(), record.args()),
+ format_args!("[{:>5}][-] {}\n", record.level(), record.args()),
ConsoleColor::from(record.level()),
);
}
diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs
index d4bff95..a27f236 100644
--- a/kernel/src/memory.rs
+++ b/kernel/src/memory.rs
@@ -16,7 +16,7 @@ use super::HEAP_ALLOCATOR;
pub use crate::arch::paging::*;
use crate::consts::{KERNEL_OFFSET, MEMORY_OFFSET};
use crate::process::current_thread;
-use crate::sync::SpinNoIrqLock;
+use crate::sync::{SpinNoIrqLock, MutexGuard, SpinNoIrq};
use alloc::boxed::Box;
use bitmap_allocator::BitAlloc;
use buddy_system_allocator::Heap;
@@ -52,24 +52,19 @@ pub type FrameAlloc = bitmap_allocator::BitAlloc4K;
lazy_static! {
pub static ref FRAME_ALLOCATOR: SpinNoIrqLock =
SpinNoIrqLock::new(FrameAlloc::default());
+ pub static ref ACTIVE_TABLE: SpinNoIrqLock =
+ SpinNoIrqLock::new(unsafe { ActivePageTable::new() });
}
-/// The only way to get active page table
+/// The only way to get current active page table safely
///
-/// ## CHANGE LOG
-///
-/// In the past, this function returns a `MutexGuard` of a global
-/// `Mutex` object, which means only one CPU core
-/// can access its active table at a time.
-///
-/// But given that a page table is ** process local **, and being active
-/// when and only when a thread of the process is running.
-/// The ownership of this page table is in the `MemorySet` object.
-/// So it's safe to access the active table inside `MemorySet`.
-/// But the shared parts is readonly, e.g. all pages mapped in
-/// `InactivePageTable::map_kernel()`.
-pub fn active_table() -> ActivePageTable {
- unsafe { ActivePageTable::new() }
+/// NOTE:
+/// Current implementation of recursive page table has a problem that
+/// will cause race condition to the initial page table.
+/// So we have to add a global mutex to avoid the racing.
+/// This will be removed after replacing recursive mapping by linear mapping.
+pub fn active_table() -> MutexGuard<'static, ActivePageTable, SpinNoIrq> {
+ ACTIVE_TABLE.lock()
}
#[derive(Debug, Clone, Copy)]
diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs
index 3f7c272..f19e145 100644
--- a/kernel/src/process/structs.rs
+++ b/kernel/src/process/structs.rs
@@ -22,6 +22,7 @@ use crate::sync::{Condvar, SpinNoIrqLock as Mutex};
use super::abi::{self, ProcInitInfo};
use core::mem::uninitialized;
use rcore_fs::vfs::INode;
+use crate::processor;
pub struct Thread {
context: Context,
@@ -66,7 +67,7 @@ pub struct Process {
// relationship
pub pid: Pid, // i.e. tgid, usually the tid of first thread
- pub parent: Option>>,
+ pub parent: Weak>,
pub children: Vec>>,
pub threads: Vec, // threads in the same process
@@ -125,7 +126,7 @@ impl Thread {
exec_path: String::new(),
futexes: BTreeMap::default(),
pid: Pid(0),
- parent: None,
+ parent: Weak::new(),
children: Vec::new(),
threads: Vec::new(),
child_exit: Arc::new(Condvar::new()),
@@ -303,7 +304,7 @@ impl Thread {
exec_path: String::from(exec_path),
futexes: BTreeMap::default(),
pid: Pid(0),
- parent: None,
+ parent: Weak::new(),
children: Vec::new(),
threads: Vec::new(),
child_exit: Arc::new(Condvar::new()),
@@ -329,7 +330,7 @@ impl Thread {
exec_path: proc.exec_path.clone(),
futexes: BTreeMap::default(),
pid: Pid(0),
- parent: Some(self.proc.clone()),
+ parent: Arc::downgrade(&self.proc),
children: Vec::new(),
threads: Vec::new(),
child_exit: Arc::new(Condvar::new()),
@@ -403,6 +404,20 @@ impl Process {
}
self.futexes.get(&uaddr).unwrap().clone()
}
+ /// Exit the process.
+ /// Kill all threads and notify parent with the exit code.
+ pub fn exit(&mut self, exit_code: usize) {
+ // quit all threads
+ for tid in self.threads.iter() {
+ processor().manager().exit(*tid, 1);
+ }
+ // notify parent and fill exit code
+ if let Some(parent) = self.parent.upgrade() {
+ let mut parent = parent.lock();
+ parent.child_exit_code.insert(self.pid.get(), exit_code);
+ parent.child_exit.notify_one();
+ }
+ }
}
trait ToMemoryAttr {
diff --git a/kernel/src/sync/mutex.rs b/kernel/src/sync/mutex.rs
index 8af1460..623bac5 100644
--- a/kernel/src/sync/mutex.rs
+++ b/kernel/src/sync/mutex.rs
@@ -28,6 +28,7 @@
use super::Condvar;
use crate::arch::interrupt;
+use crate::processor;
use core::cell::UnsafeCell;
use core::fmt;
use core::ops::{Deref, DerefMut};
@@ -35,11 +36,12 @@ use core::sync::atomic::{AtomicBool, Ordering};
pub type SpinLock = Mutex;
pub type SpinNoIrqLock = Mutex;
-pub type ThreadLock = Mutex;
+pub type SleepLock = Mutex;
pub struct Mutex {
lock: AtomicBool,
support: S,
+ user: UnsafeCell<(usize, usize)>, // (cid, tid)
data: UnsafeCell,
}
@@ -78,6 +80,7 @@ impl Mutex {
lock: AtomicBool::new(false),
data: UnsafeCell::new(user_data),
support: S::new(),
+ user: UnsafeCell::new((0, 0)),
}
}
@@ -93,11 +96,23 @@ impl Mutex {
impl Mutex {
fn obtain_lock(&self) {
while self.lock.compare_and_swap(false, true, Ordering::Acquire) != false {
+ let mut try_count = 0;
// Wait until the lock looks unlocked before retrying
while self.lock.load(Ordering::Relaxed) {
self.support.cpu_relax();
+ try_count += 1;
+ if try_count == 0x100000 {
+ let (cid, tid) = unsafe { *self.user.get() };
+ error!(
+ "Mutex: deadlock detected! locked by cpu {} thread {} @ {:?}",
+ cid, tid, self as *const Self
+ );
+ }
}
}
+ let cid = crate::arch::cpu::id();
+ let tid = processor().tid_option().unwrap_or(0);
+ unsafe { self.user.get().write((cid, tid)) };
}
/// Locks the spinlock and returns a guard.
diff --git a/kernel/src/sync/test.rs b/kernel/src/sync/test.rs
index 3b7e1c7..76196e8 100644
--- a/kernel/src/sync/test.rs
+++ b/kernel/src/sync/test.rs
@@ -3,7 +3,7 @@
//! The code is borrowed from [RustDoc - Dining Philosophers](https://doc.rust-lang.org/1.6.0/book/dining-philosophers.html)
use crate::sync::Condvar;
-use crate::sync::ThreadLock as Mutex;
+use crate::sync::SleepLock as Mutex;
use crate::thread;
use alloc::vec;
use alloc::{sync::Arc, vec::Vec};
diff --git a/kernel/src/syscall/fs.rs b/kernel/src/syscall/fs.rs
index 401a950..35f0a4f 100644
--- a/kernel/src/syscall/fs.rs
+++ b/kernel/src/syscall/fs.rs
@@ -141,6 +141,9 @@ impl Syscall<'_> {
return Ok(0);
}
+ // NOTE: To run rustc, uncomment yield_now and comment Condvar.
+ // Waking up from pipe is unimplemented now.
+ // thread::yield_now();
Condvar::wait_any(&[&STDIN.pushed, &(*SOCKET_ACTIVITY)]);
}
}
@@ -524,7 +527,7 @@ impl Syscall<'_> {
arg3: usize,
) -> SysResult {
info!(
- "ioctl: fd: {}, request: {:x}, args: {} {} {}",
+ "ioctl: fd: {}, request: {:#x}, args: {:#x} {:#x} {:#x}",
fd, request, arg1, arg2, arg3
);
let mut proc = self.process();
@@ -1416,6 +1419,7 @@ impl IoVecs {
}
#[repr(C)]
+#[derive(Debug)]
pub struct PollFd {
fd: u32,
events: PollEvents,
diff --git a/kernel/src/syscall/misc.rs b/kernel/src/syscall/misc.rs
index 119cf8b..df0dec0 100644
--- a/kernel/src/syscall/misc.rs
+++ b/kernel/src/syscall/misc.rs
@@ -69,10 +69,9 @@ impl Syscall<'_> {
val,
timeout
);
- // if op & OP_PRIVATE == 0 {
- // unimplemented!("futex only support process-private");
- // return Err(SysError::ENOSYS);
- // }
+ if op & OP_PRIVATE == 0 {
+ warn!("process-shared futex is unimplemented");
+ }
if uaddr % size_of::() != 0 {
return Err(SysError::EINVAL);
}
@@ -80,7 +79,7 @@ impl Syscall<'_> {
const OP_WAIT: u32 = 0;
const OP_WAKE: u32 = 1;
- const OP_PRIVATE: u32 = 128;
+ const OP_PRIVATE: u32 = 0x80;
let mut proc = self.process();
let queue = proc.get_futex(uaddr);
diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs
index e5269db..89cb0f4 100644
--- a/kernel/src/syscall/proc.rs
+++ b/kernel/src/syscall/proc.rs
@@ -54,7 +54,6 @@ impl Syscall<'_> {
let new_thread = self
.thread
.clone(self.tf, newsp, newtls, child_tid as usize);
- // FIXME: parent pid
let tid = processor().manager().add(new_thread);
processor().manager().detach(tid);
info!("clone: {} -> {}", thread::current().id(), tid);
@@ -66,7 +65,7 @@ impl Syscall<'_> {
/// Wait for the process exit.
/// Return the PID. Store exit code to `wstatus` if it's not null.
pub fn sys_wait4(&mut self, pid: isize, wstatus: *mut i32) -> SysResult {
- //info!("wait4: pid: {}, code: {:?}", pid, wstatus);
+ info!("wait4: pid: {}, code: {:?}", pid, wstatus);
let wstatus = if !wstatus.is_null() {
Some(unsafe { self.vm().check_write_ptr(wstatus)? })
} else {
@@ -75,10 +74,12 @@ impl Syscall<'_> {
#[derive(Debug)]
enum WaitFor {
AnyChild,
+ AnyChildInGroup,
Pid(usize),
}
let target = match pid {
- -1 | 0 => WaitFor::AnyChild,
+ -1 => WaitFor::AnyChild,
+ 0 => WaitFor::AnyChildInGroup,
p if p > 0 => WaitFor::Pid(p as usize),
_ => unimplemented!(),
};
@@ -86,7 +87,7 @@ impl Syscall<'_> {
let mut proc = self.process();
// check child_exit_code
let find = match target {
- WaitFor::AnyChild => proc
+ WaitFor::AnyChild | WaitFor::AnyChildInGroup => proc
.child_exit_code
.iter()
.next()
@@ -102,17 +103,19 @@ impl Syscall<'_> {
return Ok(pid);
}
// if not, check pid
- let children: Vec<_> = proc
- .children
- .iter()
- .filter_map(|weak| weak.upgrade())
- .collect();
- let invalid = match target {
- WaitFor::AnyChild => children.len() == 0,
- WaitFor::Pid(pid) => children
+ let invalid = {
+ let children: Vec<_> = proc
+ .children
.iter()
- .find(|p| p.lock().pid.get() == pid)
- .is_none(),
+ .filter_map(|weak| weak.upgrade())
+ .collect();
+ match target {
+ WaitFor::AnyChild | WaitFor::AnyChildInGroup => children.len() == 0,
+ WaitFor::Pid(pid) => children
+ .iter()
+ .find(|p| p.lock().pid.get() == pid)
+ .is_none(),
+ }
};
if invalid {
return Err(SysError::ECHILD);
@@ -204,7 +207,7 @@ impl Syscall<'_> {
/// Kill the process
pub fn sys_kill(&mut self, pid: usize, sig: usize) -> SysResult {
info!(
- "kill: {} killed: {} with sig {}",
+ "kill: thread {} kill process {} with signal {}",
thread::current().id(),
pid,
sig
@@ -215,21 +218,8 @@ impl Syscall<'_> {
self.sys_exit_group(sig);
} else {
if let Some(proc_arc) = PROCESSES.read().get(&pid).and_then(|weak| weak.upgrade()) {
- let proc = proc_arc.lock();
- // quit all threads
- for tid in proc.threads.iter() {
- processor().manager().exit(*tid, sig);
- }
- // notify parent and fill exit code
- // avoid deadlock
- let proc_parent = proc.parent.clone();
- let pid = proc.pid.get();
- drop(proc);
- if let Some(parent) = proc_parent {
- let mut parent = parent.lock();
- parent.child_exit_code.insert(pid, sig);
- parent.child_exit.notify_one();
- }
+ let mut proc = proc_arc.lock();
+ proc.exit(sig);
Ok(0)
} else {
Err(SysError::EINVAL)
@@ -252,7 +242,7 @@ impl Syscall<'_> {
/// Get the parent process id
pub fn sys_getppid(&mut self) -> SysResult {
- if let Some(parent) = self.process().parent.as_ref() {
+ if let Some(parent) = self.process().parent.upgrade() {
Ok(parent.lock().pid.get())
} else {
Ok(0)
@@ -266,26 +256,15 @@ impl Syscall<'_> {
let mut proc = self.process();
proc.threads.retain(|&id| id != tid);
- // for last thread,
- // notify parent and fill exit code
- // avoid deadlock
- let exit = proc.threads.len() == 0;
- let proc_parent = proc.parent.clone();
- let pid = proc.pid.get();
- drop(proc);
- if exit {
- if let Some(parent) = proc_parent {
- let mut parent = parent.lock();
- parent.child_exit_code.insert(pid, exit_code);
- parent.child_exit.notify_one();
- }
+ // for last thread, exit the process
+ if proc.threads.len() == 0 {
+ proc.exit(exit_code);
}
// perform futex wake 1
// ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html
// FIXME: do it in all possible ways a thread can exit
// it has memory access so we can't move it to Thread::drop?
- let mut proc = self.process();
let clear_child_tid = self.thread.clear_child_tid as *mut u32;
if !clear_child_tid.is_null() {
info!("exit: futex {:#?} wake 1", clear_child_tid);
@@ -304,24 +283,10 @@ impl Syscall<'_> {
/// Exit the current thread group (i.e. process)
pub fn sys_exit_group(&mut self, exit_code: usize) -> ! {
- let proc = self.process();
+ let mut proc = self.process();
info!("exit_group: {}, code: {}", proc.pid, exit_code);
- // quit all threads
- for tid in proc.threads.iter() {
- processor().manager().exit(*tid, exit_code);
- }
-
- // notify parent and fill exit code
- // avoid deadlock
- let proc_parent = proc.parent.clone();
- let pid = proc.pid.get();
- drop(proc);
- if let Some(parent) = proc_parent {
- let mut parent = parent.lock();
- parent.child_exit_code.insert(pid, exit_code);
- parent.child_exit.notify_one();
- }
+ proc.exit(exit_code);
processor().yield_now();
unreachable!();
diff --git a/user b/user
index 9fb1d45..ad822e6 160000
--- a/user
+++ b/user
@@ -1 +1 @@
-Subproject commit 9fb1d459b50bc14c7ac56d9fd94b4b8485620730
+Subproject commit ad822e6d3b626b598874bb52a407e90f549c5ab9