Skip to content
Closed
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
32 changes: 20 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ RS = src/*.rs

# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
TOOLPREFIX := $(shell if command -v i386-jos-elf-gcc >/dev/null 2>&1 && i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
elif command -v i686-elf-gcc >/dev/null 2>&1 && i686-elf-objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo 'i686-elf-'; \
elif command -v gcc >/dev/null 2>&1 && objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \
Expand Down Expand Up @@ -69,19 +71,25 @@ xv6.img: bootblock kernel
dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc

mkfs: src/mkfs.rs
rustc -W warnings -o mkfs src/mkfs.rs

fs.img: mkfs welcome.txt
./mkfs fs.img welcome.txt

bootblock: bootasm.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
$(OBJDUMP) -S -D bootblock.o > bootblock.asm
$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
./sign.pl bootblock
perl sign.pl bootblock

kernel.a: $(RS)
cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a
cargo rustc -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec --target ./targets/i686.json --lib --release -- -A warnings --emit link=kernel.a

kernel: kernel.a $(OBJS) ./linkers/kernel.ld
ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a
$(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a
$(OBJDUMP) -S -D kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym

Expand All @@ -100,12 +108,12 @@ vectors.S: vectors.pl

clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
*.a *.o *.d *.asm *.sym bootblock kernel xv6.img .gdbinit vectors.S
rm -r target
*.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs
rm -rf target

# run in emulators
# try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
GDBPORT = $(shell expr `id -u` % 5000 + 25001)
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \
Expand All @@ -116,14 +124,14 @@ endif

# For debugging
# QEMUEXTRA = -no-reboot -d int,cpu_reset
QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)
QEMUOPTS = -drive file=xv6.img,index=0,media=disk,format=raw -drive file=fs.img,index=1,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)

qemu: xv6.img
qemu: xv6.img fs.img
$(QEMU) -nographic $(QEMUOPTS)

.gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@

qemu-gdb: xv6.img .gdbinit
qemu-gdb: xv6.img .gdbinit fs.img
@echo "*** Now run 'gdb'." 1>&2
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
5 changes: 0 additions & 5 deletions README

This file was deleted.

50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# COL331RS: p11-file-layer (Rust)

This branch ports the `p11-file-layer` stage from the C repo (`codenet/col331`) into Rust in `codenet/col331rs` style.

## What This Branch Adds

- Rust file descriptor layer (`src/file.rs`):
- `filealloc`, `filedup`, `fileclose`, `filestat`, `fileread`, `filewrite`
- pathname/file ops: `open`, `create`, `unlink`, `isdirempty`
- Open-mode constants (`src/fcntl.rs`):
- `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_CREATE`
- File-system lifecycle changes in `src/fs.rs`:
- block free path (`bfree`)
- inode truncation (`itrunc`)
- reference-drop semantics (`iput`) matching p11 behavior
- updated read/path behavior to work with unlink + final `iput`
- Rust `mkfs` integration (`src/mkfs.rs`):
- no dependency on external `../col331/mkfs.c`
- Boot demo update (`src/lib.rs`):
- creates `/foo/hello.txt`, writes and reads it, unlinks it, verifies directory emptiness
- reads `/welcome.txt` through the file layer

## Build And Run

Make sure your cross-toolchain is available (for example `TOOLPREFIX=i686-elf-`).

```bash
make clean
make xv6.img fs.img
make qemu
```

## Source Alignment

- C reference branch: `codenet/col331` -> `p11-file-layer`
- Rust base used: p10 Rust work, then extended to p11 file layer behavior

## Credits

This branch reuses and builds on earlier COL331RS community work:

- **Amber-Agarwal** (`refs/pull/7/head` in `codenet/col331rs`)
- p10-level Rust filesystem write path and directory/name operation groundwork
- **Nipun Goel** (aka **sudoheckbeluga**, `refs/pull/5/head`)
- early Rust `mkfs` implementation used as the basis for later versions
- **legends1307** (`refs/pull/12/head`)
- self-contained Rust `mkfs` adaptation (`FSSIZE=1000`, `LOGSIZE=0`) and related integration improvements
- **COL331 course/staff repository authors** (`codenet/col331`, `codenet/col331rs`)
- original xv6 step-by-step structure and assignment progression

148 changes: 148 additions & 0 deletions src/bio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use core::sync::atomic::Ordering;

use crate::buf::{Buf, B_DIRTY, B_VALID, NBUF};

const HEAD: usize = NBUF; // sentinel index

struct BCache {
buf: [Buf; NBUF],
head_prev: usize,
head_next: usize,
}

impl BCache {
pub const fn new() -> Self {
Self {
buf: [const { Buf::new() }; NBUF],
head_prev: HEAD,
head_next: HEAD,
}
}
}

static mut BCACHE: BCache = BCache::new();

pub fn binit() {
unsafe {
// empty list
BCACHE.head_prev = HEAD;
BCACHE.head_next = HEAD;

// insert all buffers at head (MRU side)
for i in 0..NBUF {
insert_at_head(i);
}
}
}

#[inline]
fn insert_at_head(i: usize) {
unsafe {
let first = BCACHE.head_next;

BCACHE.buf[i].prev = HEAD;
BCACHE.buf[i].next = first;

if first == HEAD {
// list was empty
BCACHE.head_prev = i;
} else {
BCACHE.buf[first].prev = i;
}

BCACHE.head_next = i;
}
}

#[inline]
fn remove_from_list(i: usize) {
unsafe {
let prev = BCACHE.buf[i].prev;
let next = BCACHE.buf[i].next;

if prev == HEAD {
BCACHE.head_next = next;
} else {
BCACHE.buf[prev].next = next;
}

if next == HEAD {
BCACHE.head_prev = prev;
} else {
BCACHE.buf[next].prev = prev;
}
}
}

// Return mutable buf by index.
// Safe to call only when you “own” the buffer logically (like xv6 “locked buf”).
pub fn buf_mut(idx: usize) -> &'static mut Buf {
unsafe { &mut BCACHE.buf[idx] }
}

// Look for cached block; else recycle an unused non-dirty buffer.
fn bget(dev: u32, blockno: u32) -> usize {
unsafe {
// Is the block already cached?
let mut b = BCACHE.head_next;
while b != HEAD {
if BCACHE.buf[b].dev == dev && BCACHE.buf[b].blockno == blockno {
BCACHE.buf[b].refcnt += 1;
return b;
}
b = BCACHE.buf[b].next;
}

// Not cached; recycle from LRU end.
let mut b = BCACHE.head_prev;
while b != HEAD {
let flags = BCACHE.buf[b].flags.load(Ordering::Acquire);
if BCACHE.buf[b].refcnt == 0 && (flags & B_DIRTY) == 0 {
BCACHE.buf[b].dev = dev;
BCACHE.buf[b].blockno = blockno;
BCACHE.buf[b].flags.store(0, Ordering::Release);
BCACHE.buf[b].refcnt = 1;
BCACHE.buf[b].qnext = None;
return b;
}
b = BCACHE.buf[b].prev;
}

panic!("bget: no buffers");
}
}

// Return buffer index with contents of block.
pub fn bread(dev: u32, blockno: u32) -> usize {
let idx = bget(dev, blockno);

let flags = buf_mut(idx).flags.load(Ordering::Acquire);
if (flags & B_VALID) == 0 {
crate::ide::iderw(idx);
}

idx
}

// Mark dirty + write to disk.
pub fn bwrite(idx: usize) {
let b = buf_mut(idx);
b.flags.fetch_or(B_DIRTY, Ordering::AcqRel);
crate::ide::iderw(idx);
}

// Release buffer. If refcnt hits 0, move to MRU head.
pub fn brelse(idx: usize) {
unsafe {
let b = &mut BCACHE.buf[idx];
if b.refcnt == 0 {
panic!("brelse: refcnt underflow");
}

b.refcnt -= 1;
if b.refcnt == 0 {
remove_from_list(idx);
insert_at_head(idx);
}
}
}
39 changes: 39 additions & 0 deletions src/buf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use core::sync::atomic::AtomicU32;

pub const BSIZE: usize = 512; // block size
pub const NBUF: usize = 30; // same as xv6 default; can tune later

pub const B_VALID: u32 = 0x2; // buffer has been read from disk
pub const B_DIRTY: u32 = 0x4; // buffer needs to be written to disk

#[repr(C)]
pub struct Buf {
pub flags: AtomicU32,
pub dev: u32,
pub blockno: u32,
pub refcnt: u32,

// LRU list (intrusive, by index)
pub prev: usize,
pub next: usize,

// disk queue (by index)
pub qnext: Option<usize>,

pub data: [u8; BSIZE],
}

impl Buf {
pub const fn new() -> Self {
Self {
flags: AtomicU32::new(0),
dev: 0,
blockno: 0,
refcnt: 0,
prev: 0,
next: 0,
qnext: None,
data: [0; BSIZE],
}
}
}
Loading