Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions kernel/src/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,7 @@ pub fn set_kernel_stack(stack_top: VirtAddr) {
let tss_ptr = TSS_PTR.load(Ordering::Acquire);
if !tss_ptr.is_null() {
unsafe {
let old_stack = (*tss_ptr).privilege_stack_table[0];
(*tss_ptr).privilege_stack_table[0] = stack_top;
crate::serial_println!(
"TSS RSP0 updated: {:#x} -> {:#x}",
old_stack.as_u64(),
stack_top.as_u64()
);
}
} else {
panic!("TSS not initialized");
Expand Down
27 changes: 23 additions & 4 deletions kernel/src/ipc/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,24 @@ impl Clone for FdTable {

let cloned_fds = alloc::boxed::Box::new((*self.fds).clone());

// Increment pipe reference counts for all cloned pipe fds
// Increment reference counts for all cloned fds that need it
for fd_opt in cloned_fds.iter() {
if let Some(fd_entry) = fd_opt {
match &fd_entry.kind {
FdKind::PipeRead(buffer) => buffer.lock().add_reader(),
FdKind::PipeWrite(buffer) => buffer.lock().add_writer(),
FdKind::PtyMaster(pty_num) => {
// Increment PTY master reference count for the clone
if let Some(pair) = crate::tty::pty::get(*pty_num) {
let old_count = pair.master_refcount.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
log::debug!("FdTable::clone() - PTY master {} refcount {} -> {}",
pty_num, old_count, old_count + 1);
}
}
FdKind::TcpConnection(conn_id) => {
// Increment TCP connection reference count for the clone
crate::net::tcp::tcp_add_ref(conn_id);
}
_ => {}
}
}
Expand Down Expand Up @@ -470,9 +482,16 @@ impl Drop for FdTable {
log::debug!("FdTable::drop() - releasing devpts directory fd {}", i);
}
FdKind::PtyMaster(pty_num) => {
// PTY master cleanup - release the PTY pair when master closes
crate::tty::pty::release(pty_num);
log::debug!("FdTable::drop() - released PTY master fd {} (pty {})", i, pty_num);
// PTY master cleanup - decrement refcount, only release when all masters closed
if let Some(pair) = crate::tty::pty::get(pty_num) {
let old_count = pair.master_refcount.fetch_sub(1, core::sync::atomic::Ordering::SeqCst);
log::debug!("FdTable::drop() - PTY master fd {} (pty {}) refcount {} -> {}",
i, pty_num, old_count, old_count - 1);
if old_count == 1 {
crate::tty::pty::release(pty_num);
log::debug!("FdTable::drop() - released PTY {} (last master closed)", pty_num);
}
}
}
FdKind::PtySlave(_pty_num) => {
// PTY slave doesn't own the pair, just decrement reference
Expand Down
23 changes: 22 additions & 1 deletion kernel/src/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ pub struct TcpConnection {
pub send_shutdown: bool,
/// True if SHUT_RD was called (no more receiving)
pub recv_shutdown: bool,
/// Reference count for fork() support - connection is only closed when last fd is closed
pub refcount: core::sync::atomic::AtomicUsize,
}

impl TcpConnection {
Expand All @@ -321,6 +323,7 @@ impl TcpConnection {
owner_pid,
send_shutdown: false,
recv_shutdown: false,
refcount: core::sync::atomic::AtomicUsize::new(1),
}
}

Expand Down Expand Up @@ -352,6 +355,7 @@ impl TcpConnection {
owner_pid,
send_shutdown: false,
recv_shutdown: false,
refcount: core::sync::atomic::AtomicUsize::new(1),
}
}
}
Expand Down Expand Up @@ -1034,13 +1038,30 @@ pub fn tcp_shutdown(conn_id: &ConnectionId, shut_rd: bool, shut_wr: bool) {
}
}

/// Close a connection
/// Increment reference count on a TCP connection (called during fork)
pub fn tcp_add_ref(conn_id: &ConnectionId) {
let connections = TCP_CONNECTIONS.lock();
if let Some(conn) = connections.get(conn_id) {
conn.refcount.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
}
}

/// Close a connection (decrement refcount, only actually close when last reference dropped)
pub fn tcp_close(conn_id: &ConnectionId) -> Result<(), &'static str> {
let config = super::config();

let mut connections = TCP_CONNECTIONS.lock();
let conn = connections.get_mut(conn_id).ok_or("Connection not found")?;

// Decrement reference count
let old_count = conn.refcount.fetch_sub(1, core::sync::atomic::Ordering::SeqCst);

// Only actually close when last reference is dropped
if old_count > 1 {
return Ok(());
}

// Last reference - actually close the connection
match conn.state {
TcpState::Established => {
// Send FIN
Expand Down
13 changes: 10 additions & 3 deletions kernel/src/process/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,16 @@ impl Process {
log::debug!("Process::close_all_fds() - released devpts directory fd {}", fd);
}
FdKind::PtyMaster(pty_num) => {
// PTY master cleanup - release the PTY pair when master closes
crate::tty::pty::release(pty_num);
log::debug!("Process::close_all_fds() - released PTY master fd {} (pty {})", fd, pty_num);
// PTY master cleanup - decrement refcount, only release when all masters closed
if let Some(pair) = crate::tty::pty::get(pty_num) {
let old_count = pair.master_refcount.fetch_sub(1, core::sync::atomic::Ordering::SeqCst);
log::debug!("Process::close_all_fds() - PTY master fd {} (pty {}) refcount {} -> {}",
fd, pty_num, old_count, old_count - 1);
if old_count == 1 {
crate::tty::pty::release(pty_num);
log::debug!("Process::close_all_fds() - released PTY {} (last master closed)", pty_num);
}
}
}
FdKind::PtySlave(_pty_num) => {
// PTY slave doesn't own the pair, just decrement reference
Expand Down
15 changes: 14 additions & 1 deletion kernel/src/syscall/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@ pub fn sys_read(fd: u64, buf_ptr: u64, count: u64) -> SyscallResult {
}
FdKind::TcpConnection(conn_id) => {
// Read from TCP connection
// First drain loopback queue in case there are pending packets
// First process any pending network packets
crate::net::process_rx();
crate::net::drain_loopback_queue();
let mut user_buf = alloc::vec![0u8; count as usize];
match crate::net::tcp::tcp_recv(conn_id, &mut user_buf) {
Expand Down Expand Up @@ -2301,6 +2302,12 @@ pub fn sys_poll(fds_ptr: u64, nfds: u64, _timeout: i32) -> SyscallResult {

log::debug!("sys_poll: fds_ptr={:#x}, nfds={}, timeout={}", fds_ptr, nfds, _timeout);

// Process any pending network packets before checking fd readiness.
// This is critical for TCP listeners - without this, incoming SYN packets
// remain unprocessed in the e1000 driver buffer and accept() never sees connections.
crate::net::process_rx();
crate::net::drain_loopback_queue();

// Validate parameters
if fds_ptr == 0 && nfds > 0 {
return SyscallResult::Err(14); // EFAULT
Expand Down Expand Up @@ -2429,6 +2436,12 @@ pub fn sys_select(
nfds, readfds_ptr, writefds_ptr, exceptfds_ptr, _timeout_ptr
);

// Process any pending network packets before checking fd readiness.
// This is critical for TCP listeners - without this, incoming SYN packets
// remain unprocessed in the e1000 driver buffer and accept() never sees connections.
crate::net::process_rx();
crate::net::drain_loopback_queue();

// Validate nfds - must be non-negative and <= 64 (we only support u64 bitmaps)
if nfds < 0 {
log::debug!("sys_select: Invalid nfds {}", nfds);
Expand Down
16 changes: 13 additions & 3 deletions kernel/src/syscall/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,19 @@ pub fn sys_close(fd: i32) -> SyscallResult {
log::debug!("sys_close: Closed TCP connection fd={}", fd);
}
FdKind::PtyMaster(pty_num) => {
// PTY master cleanup - release the PTY pair when master closes
crate::tty::pty::release(pty_num);
log::debug!("sys_close: Closed PTY master fd={} (pty {})", fd, pty_num);
// PTY master cleanup - decrement refcount, only release when all masters closed
if let Some(pair) = crate::tty::pty::get(pty_num) {
let old_count = pair.master_refcount.fetch_sub(1, core::sync::atomic::Ordering::SeqCst);
log::debug!("sys_close: PTY master fd={} (pty {}) refcount {} -> {}",
fd, pty_num, old_count, old_count - 1);
if old_count == 1 {
// Last reference - release the PTY pair
crate::tty::pty::release(pty_num);
log::debug!("sys_close: Released PTY {} (last master closed)", pty_num);
}
} else {
log::warn!("sys_close: PTY {} not found for master fd={}", pty_num, fd);
}
}
FdKind::PtySlave(pty_num) => {
// PTY slave doesn't own the pair, just log closure
Expand Down
9 changes: 9 additions & 0 deletions kernel/src/syscall/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ pub fn sys_recvfrom(
src_addr_ptr: u64,
addrlen_ptr: u64,
) -> SyscallResult {
// Process any pending network packets before checking for received data.
crate::net::process_rx();
crate::net::drain_loopback_queue();

// Validate buffer pointer
if buf_ptr == 0 {
return SyscallResult::Err(EFAULT as u64);
Expand Down Expand Up @@ -471,6 +475,11 @@ pub fn sys_listen(fd: u64, backlog: u64) -> SyscallResult {
pub fn sys_accept(fd: u64, addr_ptr: u64, addrlen_ptr: u64) -> SyscallResult {
log::debug!("sys_accept: fd={}", fd);

// Process any pending network packets before checking for connections.
// This ensures incoming SYN packets are handled even if IRQ hasn't fired.
crate::net::process_rx();
crate::net::drain_loopback_queue();

// Get current thread and process
let current_thread_id = match crate::per_cpu::current_thread() {
Some(thread) => thread.id,
Expand Down
9 changes: 7 additions & 2 deletions run.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#!/bin/bash
# Start Breenix in interactive mode
cargo run -p xtask -- interactive "$@"
# Start Breenix in interactive mode with telnet port forwarding (2323)
# Port 2323 is forwarded automatically by qemu-uefi
#
# Two windows:
# - QEMU window: Type commands here (PS/2 keyboard -> shell)
# - This terminal: Watch serial output (debug messages)
cargo run --release --features testing,external_test_bins,interactive --bin qemu-uefi -- -display cocoa -serial mon:stdio "$@"
34 changes: 34 additions & 0 deletions scripts/create_ext2_disk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ if [[ "$(uname)" == "Darwin" ]]; then
echo " WARNING: hello_world.elf not found"
fi

# Copy init_shell for interactive use and telnet
if [ -f /binaries/init_shell.elf ]; then
cp /binaries/init_shell.elf /mnt/ext2/bin/init_shell
chmod 755 /mnt/ext2/bin/init_shell
echo " /bin/init_shell installed"
else
echo " WARNING: init_shell.elf not found"
fi

# Copy telnetd for remote access (system daemon, goes in /sbin)
if [ -f /binaries/telnetd.elf ]; then
cp /binaries/telnetd.elf /mnt/ext2/sbin/telnetd
chmod 755 /mnt/ext2/sbin/telnetd
echo " /sbin/telnetd installed"
else
echo " WARNING: telnetd.elf not found"
fi

# Create test files for filesystem testing
echo "Hello from ext2!" > /mnt/ext2/hello.txt
echo "Truncate test file" > /mnt/ext2/trunctest.txt
Expand Down Expand Up @@ -209,6 +227,20 @@ else
echo " /bin/hello_world installed"
fi

# Copy init_shell for interactive use and telnet
if [ -f "$USERSPACE_DIR/init_shell.elf" ]; then
cp "$USERSPACE_DIR/init_shell.elf" "$MOUNT_DIR/bin/init_shell"
chmod 755 "$MOUNT_DIR/bin/init_shell"
echo " /bin/init_shell installed"
fi

# Copy telnetd for remote access (system daemon, goes in /sbin)
if [ -f "$USERSPACE_DIR/telnetd.elf" ]; then
cp "$USERSPACE_DIR/telnetd.elf" "$MOUNT_DIR/sbin/telnetd"
chmod 755 "$MOUNT_DIR/sbin/telnetd"
echo " /sbin/telnetd installed"
fi

# Create test files
echo "Hello from ext2!" > "$MOUNT_DIR/hello.txt"
echo "Truncate test file" > "$MOUNT_DIR/trunctest.txt"
Expand Down Expand Up @@ -265,6 +297,8 @@ if [[ -f "$OUTPUT_FILE" ]]; then
echo " /sbin/true, /bin/false - exit status coreutils"
echo " /bin/head, tail, wc, which - text processing coreutils"
echo " /bin/hello_world - exec test binary (exit code 42)"
echo " /bin/init_shell - interactive shell"
echo " /sbin/telnetd - telnet daemon"
echo " /hello.txt - test file (1 line)"
echo " /lines.txt - multi-line test file (15 lines) for head/tail/wc"
echo " /test/nested.txt - nested test file"
Expand Down
Binary file modified testdata/ext2.img
Binary file not shown.
Loading
Loading