From 514579c5863782ab85335de94fc2ca611cd560b2 Mon Sep 17 00:00:00 2001 From: ahilaan Date: Sat, 14 Feb 2026 07:51:10 +0000 Subject: [PATCH 01/69] p6: add IDE driver + buffer cache --- Makefile | 45 +++++++------ src/bio.rs | 148 +++++++++++++++++++++++++++++++++++++++++ src/buf.rs | 39 +++++++++++ src/constants.rs | 7 +- src/ide.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 30 ++++++++- src/traps.rs | 8 ++- src/x86.rs | 33 +++++++++ welcome.txt | 8 +++ 9 files changed, 459 insertions(+), 29 deletions(-) create mode 100644 src/bio.rs create mode 100644 src/buf.rs create mode 100644 src/ide.rs create mode 100644 welcome.txt diff --git a/Makefile b/Makefile index fa7b33b..9e81b2f 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ RS = src/*.rs #TOOLPREFIX = i386-elf- # Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = +#TOOLPREFIX = # Try to infer the correct TOOLPREFIX if not set ifndef TOOLPREFIX @@ -53,7 +53,6 @@ OBJDUMP = $(TOOLPREFIX)objdump CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -# FreeBSD ld wants ``elf_i386_fbsd'' LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) # Disable PIE when possible (for Ubuntu 16.10 toolchain) @@ -64,11 +63,19 @@ ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) CFLAGS += -fno-pie -nopie endif +# Disk image with bootblock + kernel xv6.img: bootblock kernel dd if=/dev/zero of=xv6.img count=10000 dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc +# Second disk (fs.img) with welcome text (2 sectors, like C version) +fs: fs.img + +fs.img: welcome.txt + dd if=/dev/zero of=fs.img count=2 + dd if=welcome.txt of=fs.img conv=notrunc + bootblock: bootasm.S bootmain.c $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S @@ -78,7 +85,13 @@ bootblock: bootasm.S bootmain.c ./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 build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ + --target ./targets/i686.json --release + @tdir=$$(cargo metadata --format-version=1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p'); \ + lib=$$(find "$$tdir" -maxdepth 4 -type f -name 'libkernel.a' | head -n 1); \ + if [ -z "$$lib" ]; then echo "ERROR: libkernel.a not found"; exit 1; fi; \ + cp "$$lib" kernel.a + kernel: kernel.a $(OBJS) ./linkers/kernel.ld ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a @@ -88,25 +101,16 @@ kernel: kernel.a $(OBJS) ./linkers/kernel.ld vectors.S: vectors.pl ./vectors.pl > vectors.S -# $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o kernel.a -b binary -# ld -m elf_i386 -T kernel.ld -o kernel entry.o kernel.a -b binary -# Prevent deletion of intermediate files, e.g. cat.o, after first build, so -# that disk image changes after first build are persistent until clean. More -# details: -# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html .PRECIOUS: %.o - -include *.d -clean: +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 + rm -rf target # run in emulators -# try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) -# QEMU's gdb stub command line changed in 0.11 QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) @@ -114,16 +118,17 @@ ifndef CPUS CPUS := 1 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) +# Attach fs.img as disk1 (index=1), like the C version +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 fs .gdbinit @echo "*** Now run 'gdb'." 1>&2 $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file diff --git a/src/bio.rs b/src/bio.rs new file mode 100644 index 0000000..a9ceb74 --- /dev/null +++ b/src/bio.rs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/buf.rs b/src/buf.rs new file mode 100644 index 0000000..2c4cfb2 --- /dev/null +++ b/src/buf.rs @@ -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, + + 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], + } + } +} \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index d06b826..b36418c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -51,17 +51,14 @@ pub const T_SIMDERR: u32 = 19; // SIMD floating point error // processor defined exceptions or interrupt vectors pub const T_SYSCALL: u32 = 64; // system call pub const T_DEFAULT: u32 = 500; // catchall - -pub const T_IRQ0: u32 = 32; // IRQ 0 corresponds to int T_IRQ - +pub const T_IRQ0: u32 = 32; pub const IRQ_TIMER: u32 = 0; pub const IRQ_KBD: u32 = 1; pub const IRQ_COM1: u32 = 4; pub const IRQ_IDE: u32 = 14; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; - - +pub const IDE_TRAP: u32 = T_IRQ0 + IRQ_IDE; // ------------------------------------------------------ MP RELATED ------------------------------------------------------- // Processor flags diff --git a/src/ide.rs b/src/ide.rs new file mode 100644 index 0000000..4ae4ee2 --- /dev/null +++ b/src/ide.rs @@ -0,0 +1,170 @@ +use core::sync::atomic::Ordering; +use crate::buf::{BSIZE, B_DIRTY, B_VALID}; +use crate::x86; + +const SECTOR_SIZE: usize = 512; + +const IDE_BSY: u8 = 0x80; +const IDE_DRDY: u8 = 0x40; +const IDE_DF: u8 = 0x20; +const IDE_ERR: u8 = 0x01; + +const IDE_CMD_READ: u8 = 0x20; +const IDE_CMD_WRITE: u8 = 0x30; +const IDE_CMD_RDMUL: u8 = 0xC4; +const IDE_CMD_WRMUL: u8 = 0xC5; + +// If you later build a real FS image, keep this consistent with your mkfs. +// xv6 uses 1000. +const FSSIZE: u32 = 1000; + +static mut IDEQUEUE: Option = None; +static mut HAVEDISK1: bool = false; + +fn idewait(checkerr: bool) -> i32 { + let mut r: u8; + loop { + r = x86::inb(0x1F7); + if (r & (IDE_BSY | IDE_DRDY)) == IDE_DRDY { + break; + } + } + if checkerr && (r & (IDE_DF | IDE_ERR)) != 0 { + return -1; + } + 0 +} + +pub fn ideinit() { + // Route IDE IRQ somewhere; simplest is CPU 0 for now. + crate::ioapic::ioapic_enable(crate::constants::IRQ_IDE, 0); + + idewait(false); + + // Check if disk 1 is present + unsafe { + x86::outb(0x1F6, 0xE0 | (1 << 4)); + for _ in 0..1000 { + if x86::inb(0x1F7) != 0 { + HAVEDISK1 = true; + break; + } + } + // Switch back to disk 0 + x86::outb(0x1F6, 0xE0 | (0 << 4)); + } +} + +fn idestart(idx: usize) { + let b = crate::bio::buf_mut(idx); + + if b.blockno >= FSSIZE { + panic!("idestart: incorrect blockno"); + } + + let sector_per_block = BSIZE / SECTOR_SIZE; // usually 1 + let sector = (b.blockno as usize) * sector_per_block; + + let read_cmd = if sector_per_block == 1 { IDE_CMD_READ } else { IDE_CMD_RDMUL }; + let write_cmd = if sector_per_block == 1 { IDE_CMD_WRITE } else { IDE_CMD_WRMUL }; + + if sector_per_block > 7 { + panic!("idestart: sector_per_block > 7"); + } + + idewait(false); + x86::outb(0x3F6, 0); // generate interrupt + + x86::outb(0x1F2, sector_per_block as u8); // number of sectors + x86::outb(0x1F3, (sector & 0xFF) as u8); + x86::outb(0x1F4, ((sector >> 8) & 0xFF) as u8); + x86::outb(0x1F5, ((sector >> 16) & 0xFF) as u8); + x86::outb( + 0x1F6, + 0xE0 | (((b.dev & 1) as u8) << 4) | (((sector >> 24) & 0x0F) as u8), + ); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & B_DIRTY) != 0 { + x86::outb(0x1F7, write_cmd); + + // write BSIZE bytes as u32 words + unsafe { + x86::outsl(0x1F0, b.data.as_ptr() as *const u32, BSIZE / 4); + } + } else { + x86::outb(0x1F7, read_cmd); + } +} + +// Interrupt handler. +pub fn ideintr() { + let idx = unsafe { + match IDEQUEUE { + None => return, + Some(i) => { + let b = crate::bio::buf_mut(i); + IDEQUEUE = b.qnext; + b.qnext = None; + i + } + } + }; + + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + + // Read data if needed. + if (flags & B_DIRTY) == 0 && idewait(true) >= 0 { + unsafe { + x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + + // Start next buffer in queue. + unsafe { + if let Some(next) = IDEQUEUE { + idestart(next); + } + } +} + +// ide.rs +pub fn iderw(idx: usize) { + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + panic!("iderw: nothing to do"); + } + + unsafe { + if b.dev != 0 && !HAVEDISK1 { + panic!("iderw: ide disk 1 not present"); + } + } + + // PURE POLLING: issue the command directly (no IDEQUEUE). + idestart(idx); + + // Wait for completion. + if idewait(true) < 0 { + panic!("iderw: ide error"); + } + + // If it was a read, pull data now. + let flags_now = b.flags.load(Ordering::Acquire); + if (flags_now & B_DIRTY) == 0 { + unsafe { + crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + // If it was a write, data was already pushed in idestart() via outsl(). + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa34772..561ebcc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,9 @@ mod mp; mod proc; mod traps; mod constants; - +mod buf; +mod bio; +mod ide; use crate::traps::*; #[macro_export] @@ -35,8 +37,28 @@ fn halt() -> ! { } } +fn welcome() { + let b0 = bio::bread(1, 0); + let data0 = bio::buf_mut(b0).data; + + for &byte in data0.iter() { + if byte == 0 { break; } + console::consputc(byte as char); + } + bio::brelse(b0); + + let b1 = bio::bread(1, 1); + let count = bio::buf_mut(b1).data[0]; + + println!("\nAfter preparing fs.img, we have rebooted {} times\n", count); + + bio::buf_mut(b1).data[0] = count.wrapping_add(1); + bio::bwrite(b1); + bio::brelse(b1); +} + extern "C" { - pub static alltraps: fn(); + pub fn alltraps(); } #[no_mangle] @@ -46,9 +68,13 @@ pub extern "C" fn entryofrust() -> ! { picirq::picinit(); ioapic::ioapic_init(); uart::uartinit(); + ide::ideinit(); tvinit(); + bio::binit(); idtinit(); x86::sti(); + welcome(); + loop { x86::wfi(); } diff --git a/src/traps.rs b/src/traps.rs index 0643fcc..6899856 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -96,7 +96,7 @@ pub fn tvinit() { 0 // Descriptor privilege level. ); } - IDT.idt.set(arr); + let _ = IDT.idt.set(arr); } pub fn idtinit() { @@ -116,7 +116,6 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const TIMER: u32 = T_IRQ0 + IRQ_TIMER; const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; - match tf.trapno { TIMER => { *IDT.ticks.borrow_mut() += 1; @@ -132,6 +131,11 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { ); lapiceoi(); } + crate::constants::IDE_TRAP => { + crate::ide::ideintr(); + crate::lapic::lapiceoi(); + } + _ => { println!( "unexpected trap {} from cpu {} eip {} (cr2=0x{:x})\n", diff --git a/src/x86.rs b/src/x86.rs index ba0598c..fb341ef 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -112,6 +112,39 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn noop() { + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); +} + +// x86.rs +pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { + core::arch::asm!( + "cld", + "rep insd", + in("dx") port, + inout("edi") (addr as usize) => _, + inout("ecx") cnt => _, + options(nostack, preserves_flags), + ); +} + + + +pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { + let mut p = addr; + for _ in 0..cnt { + let val = core::ptr::read(p); + asm!( + "out dx, eax", + in("dx") port, + in("eax") val, + options(nomem, nostack, preserves_flags), + ); + p = p.add(1); + } +} + + /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state diff --git a/welcome.txt b/welcome.txt new file mode 100644 index 0000000..3ab143e --- /dev/null +++ b/welcome.txt @@ -0,0 +1,8 @@ + ### + # # ###### # #### #### # # ###### ### + # # # # # # # # ## ## # ### + # # ##### # # # # # ## # ##### # + # ## # # # # # # # # # + ## ## # # # # # # # # # ### + # # ###### ###### #### #### # # ###### ### + From c0420075e90c028345afe28cc964b59d9bd34c61 Mon Sep 17 00:00:00 2001 From: ahilaan Date: Sat, 14 Feb 2026 08:13:05 +0000 Subject: [PATCH 02/69] p6: add IDE driver and buffer cache layer --- src/console.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/console.rs b/src/console.rs index 0f62712..12abba1 100644 --- a/src/console.rs +++ b/src/console.rs @@ -13,7 +13,7 @@ impl Write for Console { const BACKSPACE: char = '\x08'; -fn consputc(c: char) { +pub fn consputc(c: char) { if c == BACKSPACE { uartputc(BACKSPACE); uartputc(' '); From d373ec5fc0c5c64a5b2352f37427c4769a813717 Mon Sep 17 00:00:00 2001 From: Nipun Goel Date: Sat, 14 Feb 2026 15:29:13 +0530 Subject: [PATCH 03/69] mkfs implemented --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fa7b33b..34b0be2 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,12 @@ 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 *.txt + ./mkfs fs.img *.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 @@ -88,6 +94,7 @@ kernel: kernel.a $(OBJS) ./linkers/kernel.ld vectors.S: vectors.pl ./vectors.pl > vectors.S + # $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o kernel.a -b binary # ld -m elf_i386 -T kernel.ld -o kernel entry.o kernel.a -b binary # Prevent deletion of intermediate files, e.g. cat.o, after first build, so @@ -100,7 +107,7 @@ 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 + *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img mkfs .gdbinit vectors.S rm -r target # run in emulators From 6dd333f5a310fe98cf1ea5ac3bc23d88ca2f7c4e Mon Sep 17 00:00:00 2001 From: Nipun Goel Date: Sat, 14 Feb 2026 17:21:17 +0530 Subject: [PATCH 04/69] mkfs implemented --- src/mkfs.rs | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 src/mkfs.rs diff --git a/src/mkfs.rs b/src/mkfs.rs new file mode 100644 index 0000000..9bfb77b --- /dev/null +++ b/src/mkfs.rs @@ -0,0 +1,359 @@ +use std::env; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::mem::size_of; +use std::path::Path; + + +// constants to be included in constants.rs under "FILE SYSTEM" section, +// declared here for now to avoid merge conflicts + +const BSIZE: usize = 512; +const FSSIZE: u32 = 10_000; +const NINODES: u32 = 200; +const LOGSIZE: u32 = 30; + +const ROOTINO: u32 = 1; +const NDIRECT: usize = 12; +const NINDIRECT: usize = BSIZE / size_of::(); +const MAXFILE: usize = NDIRECT + NINDIRECT; +const DIRSIZ: usize = 14; + +const T_DIR: u16 = 1; +const T_FILE: u16 = 2; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct Superblock { + size: u32, + nblocks: u32, + ninodes: u32, + nlog: u32, + logstart: u32, + inodestart: u32, + bmapstart: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dinode { + typ: u16, + major: u16, + minor: u16, + nlink: u16, + size: u32, + addrs: [u32; NDIRECT + 1], +} + +impl Default for Dinode { + fn default() -> Self { + Self { + typ: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dirent { + inum: u16, + name: [u8; DIRSIZ], +} + +impl Default for Dirent { + fn default() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct Mkfs { + fsfd: File, + sb: Superblock, + freeinode: u32, + freeblock: u32, +} + +fn as_bytes(val: &T) -> &[u8] { + unsafe { std::slice::from_raw_parts((val as *const T).cast::(), size_of::()) } +} + +fn from_bytes(bytes: &[u8]) -> T { + assert_eq!(bytes.len(), size_of::()); + let mut out = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::copy_nonoverlapping(bytes.as_ptr(), out.as_mut_ptr().cast::(), bytes.len()); + out.assume_init() + } +} + +fn name_to_dirent(name: &str, inum: u16) -> Dirent { + let mut de = Dirent { + inum: inum.to_le(), + ..Default::default() + }; + let name_bytes = name.as_bytes(); + let copy_len = name_bytes.len().min(DIRSIZ); + de.name[..copy_len].copy_from_slice(&name_bytes[..copy_len]); + de +} + +impl Mkfs { + fn new(fsfd: File, sb: Superblock, freeblock: u32) -> Self { + Self { + fsfd, + sb, + freeinode: 1, + freeblock, + } + } + + fn wsect(&mut self, sec: u32, buf: &[u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(write) failed"); + self.fsfd.write_all(buf).expect("write failed"); + } + + fn rsect(&mut self, sec: u32, buf: &mut [u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(read) failed"); + self.fsfd.read_exact(buf).expect("read failed"); + } + + fn iblock(&self, inum: u32) -> u32 { + (inum / ipb() as u32) + u32::from_le(self.sb.inodestart) + } + + fn winode(&mut self, inum: u32, ip: &Dinode) { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + let src = as_bytes(ip); + buf[off..off + size_of::()].copy_from_slice(src); + self.wsect(bn, &buf); + } + + fn rinode(&mut self, inum: u32) -> Dinode { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + from_bytes::(&buf[off..off + size_of::()]) + } + + fn ialloc(&mut self, typ: u16) -> u32 { + let inum = self.freeinode; + self.freeinode += 1; + + let din = Dinode { + typ: typ.to_le(), + nlink: 1u16.to_le(), + size: 0u32.to_le(), + ..Default::default() + }; + self.winode(inum, &din); + inum + } + + fn balloc(&mut self, used: u32) { + assert!(used < (BSIZE * 8) as u32); + println!("balloc: first {} blocks have been allocated", used); + + let mut buf = [0u8; BSIZE]; + for i in 0..used { + let idx = (i / 8) as usize; + let bit = (i % 8) as u8; + buf[idx] |= 1u8 << bit; + } + + let bmapstart = u32::from_le(self.sb.bmapstart); + println!("balloc: write bitmap block at sector {}", bmapstart); + self.wsect(bmapstart, &buf); + } + + fn iappend(&mut self, inum: u32, mut p: &[u8]) { + let mut din = self.rinode(inum); + let mut off = u32::from_le(din.size); + + while !p.is_empty() { + let fbn = (off as usize) / BSIZE; + assert!(fbn < MAXFILE); + + let x: u32; + if fbn < NDIRECT { + if u32::from_le(din.addrs[fbn]) == 0 { + din.addrs[fbn] = self.freeblock.to_le(); + self.freeblock += 1; + } + x = u32::from_le(din.addrs[fbn]); + } else { + if u32::from_le(din.addrs[NDIRECT]) == 0 { + din.addrs[NDIRECT] = self.freeblock.to_le(); + self.freeblock += 1; + } + + let mut indirect_sector = [0u8; BSIZE]; + let indirect_blockno = u32::from_le(din.addrs[NDIRECT]); + self.rsect(indirect_blockno, &mut indirect_sector); + + let indirect_idx = fbn - NDIRECT; + let byte_off = indirect_idx * size_of::(); + let mut indirect_entry = u32::from_le(from_bytes::( + &indirect_sector[byte_off..byte_off + size_of::()], + )); + + if indirect_entry == 0 { + indirect_entry = self.freeblock; + self.freeblock += 1; + let le = indirect_entry.to_le(); + indirect_sector[byte_off..byte_off + size_of::()].copy_from_slice(as_bytes(&le)); + self.wsect(indirect_blockno, &indirect_sector); + } + + x = indirect_entry; + } + + let mut buf = [0u8; BSIZE]; + self.rsect(x, &mut buf); + + let n1 = p + .len() + .min((fbn as u32 + 1) as usize * BSIZE - off as usize); + let block_off = off as usize - (fbn * BSIZE); + buf[block_off..block_off + n1].copy_from_slice(&p[..n1]); + self.wsect(x, &buf); + + off += n1 as u32; + p = &p[n1..]; + } + + din.size = off.to_le(); + self.winode(inum, &din); + } +} + +fn ipb() -> usize { + BSIZE / size_of::() +} + +fn main() { + assert_eq!(size_of::(), 4, "Integers must be 4 bytes"); + assert_eq!(BSIZE % size_of::(), 0); + assert_eq!(BSIZE % size_of::(), 0); + + let mut args = env::args().collect::>(); + if args.len() < 2 { + eprintln!("Usage: mkfs fs.img files..."); + std::process::exit(1); + } + + let fs_img = args.remove(1); + + let nbitmap = FSSIZE / (BSIZE as u32 * 8) + 1; + let ninodeblocks = NINODES / ipb() as u32 + 1; + let nlog = LOGSIZE; + let nmeta = 2 + nlog + ninodeblocks + nbitmap; + let nblocks = FSSIZE - nmeta; + + let sb = Superblock { + size: FSSIZE.to_le(), + nblocks: nblocks.to_le(), + ninodes: NINODES.to_le(), + nlog: nlog.to_le(), + logstart: 2u32.to_le(), + inodestart: (2 + nlog).to_le(), + bmapstart: (2 + nlog + ninodeblocks).to_le(), + }; + + println!( + "nmeta {} (boot, super, log blocks {} inode blocks {}, bitmap blocks {}) blocks {} total {}", + nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE + ); + + let fsfd = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fs_img) + .unwrap_or_else(|e| { + eprintln!("{}: {}", fs_img, e); + std::process::exit(1); + }); + + let mut mkfs = Mkfs::new(fsfd, sb, nmeta); + + let zeroes = [0u8; BSIZE]; + for i in 0..FSSIZE { + mkfs.wsect(i, &zeroes); + } + + let mut sb_buf = [0u8; BSIZE]; + sb_buf[..size_of::()].copy_from_slice(as_bytes(&mkfs.sb)); + mkfs.wsect(1, &sb_buf); + + let rootino = mkfs.ialloc(T_DIR); + assert_eq!(rootino, ROOTINO); + + let dot = name_to_dirent(".", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dot)); + + let dotdot = name_to_dirent("..", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dotdot)); + + for path in &args[1..] { + if path.contains('/') { + eprintln!("{}: must be a basename (no /)", path); + std::process::exit(1); + } + + let mut host_file = File::open(path).unwrap_or_else(|e| { + eprintln!("{}: {}", path, e); + std::process::exit(1); + }); + + let file_name = if let Some(stripped) = path.strip_prefix('_') { + stripped + } else { + Path::new(path) + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(path) + }; + + let inum = mkfs.ialloc(T_FILE); + let de = name_to_dirent(file_name, inum as u16); + mkfs.iappend(rootino, as_bytes(&de)); + + let mut buf = [0u8; BSIZE]; + loop { + let cc = host_file.read(&mut buf).expect("read input file failed"); + if cc == 0 { + break; + } + mkfs.iappend(inum, &buf[..cc]); + } + } + + let mut din = mkfs.rinode(rootino); + let mut off = u32::from_le(din.size); + off = ((off / BSIZE as u32) + 1) * BSIZE as u32; + din.size = off.to_le(); + mkfs.winode(rootino, &din); + + mkfs.balloc(mkfs.freeblock); +} From d46c965d98a12d08eb23313948410c4622e03cd8 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sat, 14 Feb 2026 17:50:22 +0530 Subject: [PATCH 05/69] implement inode read path and welcome.txt loading from fs image --- Makefile | 24 +- rustc-ice-2026-02-14T12_17_44-2018.txt | 50 ++++ src/Makefile | 130 ++++++++++ src/bio.rs | 148 ++++++++++++ src/buf.rs | 39 +++ src/console.rs | 64 ++++- src/constants.rs | 2 +- src/fs.rs | 316 +++++++++++++++++++++++++ src/ide.rs | 170 +++++++++++++ src/lib.rs | 67 +++++- src/param.rs | 2 + src/traps.rs | 15 +- src/uart.rs | 28 ++- src/x86.rs | 33 +++ welcome.txt | 7 + 15 files changed, 1074 insertions(+), 21 deletions(-) create mode 100644 rustc-ice-2026-02-14T12_17_44-2018.txt create mode 100644 src/Makefile create mode 100644 src/bio.rs create mode 100644 src/buf.rs create mode 100644 src/fs.rs create mode 100644 src/ide.rs create mode 100644 welcome.txt diff --git a/Makefile b/Makefile index fa7b33b..0df672e 100644 --- a/Makefile +++ b/Makefile @@ -69,16 +69,22 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc +mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col331/param.h + gcc -Werror -Wall -o mkfs ../col331/mkfs.c + +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 @@ -100,12 +106,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)"; \ @@ -116,14 +122,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) \ No newline at end of file + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/rustc-ice-2026-02-14T12_17_44-2018.txt b/rustc-ice-2026-02-14T12_17_44-2018.txt new file mode 100644 index 0000000..6569374 --- /dev/null +++ b/rustc-ice-2026-02-14T12_17_44-2018.txt @@ -0,0 +1,50 @@ +thread 'rustc' panicked at /rustc-dev/7057231bd78d6c7893f905ea1832365d4c5efe17/library/core/src/slice/index.rs:1027:55: +slice index starts at 54 but ends at 52 +stack backtrace: + 0: 0x72342475904b - ::create + 1: 0x723424758f95 - ::force_capture + 2: 0x7234237516d8 - std[bef1e3e6ef206a1]::panicking::update_hook::>::{closure#0} + 3: 0x72342476bb32 - std[bef1e3e6ef206a1]::panicking::panic_with_hook + 4: 0x72342474e558 - std[bef1e3e6ef206a1]::panicking::panic_handler::{closure#0} + 5: 0x723424742ac9 - std[bef1e3e6ef206a1]::sys::backtrace::__rust_end_short_backtrace:: + 6: 0x72342474ffbd - __rustc[7b32525fab805468]::rust_begin_unwind + 7: 0x723421694bac - core[14f954927b42fbe0]::panicking::panic_fmt + 8: 0x72342272a94d - core[14f954927b42fbe0]::slice::index::slice_index_fail + 9: 0x723423089065 - ::replace + 10: 0x72342612fab5 - annotate_snippets[f15529b311271195]::renderer::render::render + 11: 0x72342634881f - ::emit_messages_default + 12: 0x723426340feb - ::emit_diagnostic + 13: 0x72342633fe94 - ::from_errors_diagnostic + 14: 0x72342633f61d - ::emit_diagnostic + 15: 0x723426367b30 - ::emit_diagnostic::{closure#3} + 16: 0x723426365aa6 - rustc_interface[5d23d87c33b7100c]::callbacks::track_diagnostic::> + 17: 0x723426364b2e - ::emit_diagnostic + 18: 0x7234263649e1 - ::emit_diagnostic + 19: 0x723421291e8c - ::emit_producing_guarantee + 20: 0x7234216efa86 - rustc_borrowck[ab19dfe50827342]::borrowck_check_region_constraints + 21: 0x723425ea9eb3 - ::do_mir_borrowck + 22: 0x723425ea472f - rustc_borrowck[ab19dfe50827342]::mir_borrowck + 23: 0x723425ea4579 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::compute_fn::__rust_begin_short_backtrace + 24: 0x72342574507e - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::, rustc_query_system[50dced63bea56a4a]::dep_graph::graph::DepNodeIndex>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> + 25: 0x723425743ba3 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace + 26: 0x723425743d65 - ::par_hir_body_owners::::{closure#0} + 27: 0x723425740c54 - rustc_interface[5d23d87c33b7100c]::passes::analysis + 28: 0x7234260504a6 - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> + 29: 0x72342605027c - rustc_query_impl[19f3565069731b2f]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 30: 0x7234261f6be7 - , rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2}>::{closure#2} as core[14f954927b42fbe0]::ops::function::FnOnce<(&rustc_session[2f7fc1337f473eb7]::session::Session, rustc_middle[8b4cd4281f9facd9]::ty::context::CurrentGcx, alloc[97538fc0341e4f07]::sync::Arc, &std[bef1e3e6ef206a1]::sync::once_lock::OnceLock, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2})>>::call_once::{shim:vtable#0} + 31: 0x72342602e0b7 - rustc_interface[5d23d87c33b7100c]::interface::run_compiler::<(), rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}>::{closure#1} + 32: 0x723426016dbe - std[bef1e3e6ef206a1]::sys::backtrace::__rust_begin_short_backtrace::::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()> + 33: 0x7234260172e0 - ::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[14f954927b42fbe0]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 34: 0x72342601816c - ::new::thread_start + 35: 0x72341f89caa4 - + 36: 0x72341f929c6c - + 37: 0x0 - + + +rustc version: 1.95.0-nightly (7057231bd 2026-02-11) +platform: x86_64-unknown-linux-gnu + +query stack during panic: +#0 [mir_borrowck] borrow-checking `welcome` +#1 [analysis] running analysis passes on crate `kernel` +end of query stack diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..079b0db --- /dev/null +++ b/src/Makefile @@ -0,0 +1,130 @@ +OBJS = entry.o vectors.o trapasm.o +RS = src/*.rs + +# Cross-compiling (e.g., on Mac OS X) +#TOOLPREFIX = i386-jos-elf +#TOOLPREFIX = i386-elf- + +# Using native tools (e.g., on X86 Linux) +#TOOLPREFIX = + +# 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; \ + then echo 'i386-jos-elf-'; \ + elif 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; \ + echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ + echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ + echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ + echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ + echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ + echo "***" 1>&2; exit 1; fi) +endif + +# If the makefile can't find QEMU, specify its path here +# QEMU = qemu-system-i386 + +# Try to infer the correct QEMU +ifndef QEMU +QEMU = $(shell if which qemu > /dev/null; \ + then echo qemu; exit; \ + elif which qemu-system-i386 > /dev/null; \ + then echo qemu-system-i386; exit; \ + elif which qemu-system-x86_64 > /dev/null; \ + then echo qemu-system-x86_64; exit; \ + else \ + qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ + if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ + echo "***" 1>&2; \ + echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ + echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ + echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ + echo "***" 1>&2; exit 1) +endif + +CC = $(TOOLPREFIX)gcc +AS = $(TOOLPREFIX)gas +LD = $(TOOLPREFIX)ld +OBJCOPY = $(TOOLPREFIX)objcopy +OBJDUMP = $(TOOLPREFIX)objdump +CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer +CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) +ASFLAGS = -m32 -gdwarf-2 -Wa,-divide +LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) + +# Disable PIE when possible (for Ubuntu 16.10 toolchain) +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) +CFLAGS += -fno-pie -no-pie +endif +ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) +CFLAGS += -fno-pie -nopie +endif + +# Disk image with bootblock + kernel +xv6.img: bootblock kernel + dd if=/dev/zero of=xv6.img count=10000 + dd if=bootblock of=xv6.img conv=notrunc + dd if=kernel of=xv6.img seek=1 conv=notrunc + +mkfs: ../../col331/mkfs.c ../../col331/fs.h ../../col331/types.h ../../col331/stat.h ../../col331/param.h + gcc -Werror -Wall -o mkfs ../../col331/mkfs.c + +fs: fs.img + +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 + perl sign.pl bootblock + +kernel.a: $(RS) + 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 + $(OBJDUMP) -S -D kernel > kernel.asm + $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym + +vectors.S: vectors.pl + ./vectors.pl > vectors.S + +.PRECIOUS: %.o +-include *.d + +clean: + rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ + *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs + rm -rf target + +# run in emulators +GDBPORT = $(shell expr `id -u` % 5000 + 25000) +QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ + then echo "-gdb tcp::$(GDBPORT)"; \ + else echo "-s -p $(GDBPORT)"; fi) +ifndef CPUS + CPUS := 1 +endif + +# Attach fs.img as disk1 (index=1), like the C version +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 fs.img + $(QEMU) -nographic $(QEMUOPTS) + +.gdbinit: .gdbinit.tmpl + sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ + +qemu-gdb: xv6.img fs .gdbinit + @echo "*** Now run 'gdb'." 1>&2 + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/bio.rs b/src/bio.rs new file mode 100644 index 0000000..30e6d81 --- /dev/null +++ b/src/bio.rs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/buf.rs b/src/buf.rs new file mode 100644 index 0000000..870a4e3 --- /dev/null +++ b/src/buf.rs @@ -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, + + 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], + } + } +} \ No newline at end of file diff --git a/src/console.rs b/src/console.rs index 0f62712..b9ea7fa 100644 --- a/src/console.rs +++ b/src/console.rs @@ -12,8 +12,27 @@ impl Write for Console { } const BACKSPACE: char = '\x08'; +const INPUT_BUF: u32 = 128; -fn consputc(c: char) { +struct Input { + buf: [u8; INPUT_BUF as usize], + r: u32, // Read index + w: u32, // Write index + e: u32, // Edit index +} + +static mut INPUT: Input = Input { + buf: [0; INPUT_BUF as usize], + r: 0, + w: 0, + e: 0, +}; + +const fn ctrl(x: u8) -> u8 { + x - b'@' +} + +pub fn consputc(c: char) { if c == BACKSPACE { uartputc(BACKSPACE); uartputc(' '); @@ -21,4 +40,45 @@ fn consputc(c: char) { } else { uartputc(c); } -} \ No newline at end of file +} + +pub fn consoleintr(getc: fn() -> i32) { + loop { + let ch_raw = getc(); + if ch_raw < 0 { + break; + } + + unsafe { + match ch_raw as u8 { + x if x == ctrl(b'U') => { + while INPUT.e != INPUT.w + && INPUT.buf[((INPUT.e - 1) % INPUT_BUF) as usize] != b'\n' + { + INPUT.e -= 1; + consputc(BACKSPACE); + } + } + x if x == ctrl(b'H') || x == 0x7f => { + if INPUT.e != INPUT.w { + INPUT.e -= 1; + consputc(BACKSPACE); + } + } + mut ch => { + if ch != 0 && INPUT.e - INPUT.r < INPUT_BUF { + if ch == b'\r' { + ch = b'\n'; + } + INPUT.buf[(INPUT.e % INPUT_BUF) as usize] = ch; + INPUT.e += 1; + consputc(ch as char); + if ch == b'\n' || ch == ctrl(b'D') || INPUT.e == INPUT.r + INPUT_BUF { + INPUT.w = INPUT.e; + } + } + } + } + } + } +} diff --git a/src/constants.rs b/src/constants.rs index d06b826..5388c0a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -60,7 +60,7 @@ pub const IRQ_COM1: u32 = 4; pub const IRQ_IDE: u32 = 14; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; - +pub const IDE_TRAP: u32 = T_IRQ0 + IRQ_IDE; // ------------------------------------------------------ MP RELATED ------------------------------------------------------- diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..8b6acaf --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,316 @@ +use core::cmp::min; + +use crate::bio; +use crate::buf::BSIZE; +use crate::param::NINODE; +use crate::println; + +pub const ROOTINO: u32 = 1; +pub const NDIRECT: usize = 12; +pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); +pub const DIRSIZ: usize = 14; +pub const DIRENT_SIZE: usize = 2 + DIRSIZ; + +const DINODE_SIZE: usize = 2 + 2 + 2 + 2 + 4 + ((NDIRECT + 1) * 4); +const IPB: u32 = (BSIZE / DINODE_SIZE) as u32; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Superblock { + pub size: u32, + pub nblocks: u32, + pub ninodes: u32, + pub nlog: u32, + pub logstart: u32, + pub inodestart: u32, + pub bmapstart: u32, +} + +impl Superblock { + pub const fn new() -> Self { + Self { + size: 0, + nblocks: 0, + ninodes: 0, + nlog: 0, + logstart: 0, + inodestart: 0, + bmapstart: 0, + } + } +} + +#[repr(C)] +pub struct Inode { + pub dev: u32, + pub inum: u32, + pub refcnt: i32, + pub valid: i32, + + pub type_: i16, + pub major: i16, + pub minor: i16, + pub nlink: i16, + pub size: u32, + pub addrs: [u32; NDIRECT + 1], +} + +impl Inode { + pub const fn new() -> Self { + Self { + dev: 0, + inum: 0, + refcnt: 0, + valid: 0, + type_: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +pub struct Stat { + pub type_: i16, + pub dev: i32, + pub ino: u32, + pub nlink: i16, + pub size: u32, +} + +impl Stat { + pub const fn new() -> Self { + Self { + type_: 0, + dev: 0, + ino: 0, + nlink: 0, + size: 0, + } + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Dirent { + pub inum: u16, + pub name: [u8; DIRSIZ], +} + +impl Dirent { + pub const fn new() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct ICache { + inode: [Inode; NINODE], +} + +impl ICache { + const fn new() -> Self { + Self { + inode: [const { Inode::new() }; NINODE], + } + } +} + +static mut SB: Superblock = Superblock::new(); +static mut ICACHE: ICache = ICache::new(); + +#[inline] +fn read_u16_le(data: &[u8], off: usize) -> u16 { + u16::from_le_bytes([data[off], data[off + 1]]) +} + +#[inline] +fn read_i16_le(data: &[u8], off: usize) -> i16 { + i16::from_le_bytes([data[off], data[off + 1]]) +} + +#[inline] +fn read_u32_le(data: &[u8], off: usize) -> u32 { + u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) +} + +#[inline] +fn iblock(inum: u32, sb: &Superblock) -> u32 { + inum / IPB + sb.inodestart +} + +pub fn parse_dirent(raw: &[u8]) -> Dirent { + let mut de = Dirent::new(); + de.inum = read_u16_le(raw, 0); + de.name.copy_from_slice(&raw[2..2 + DIRSIZ]); + de +} + +pub fn readsb(dev: u32, sb: &mut Superblock) { + let bp = bio::bread(dev, 1); + let data = &bio::buf_mut(bp).data; + + sb.size = read_u32_le(data, 0); + sb.nblocks = read_u32_le(data, 4); + sb.ninodes = read_u32_le(data, 8); + sb.nlog = read_u32_le(data, 12); + sb.logstart = read_u32_le(data, 16); + sb.inodestart = read_u32_le(data, 20); + sb.bmapstart = read_u32_le(data, 24); + + bio::brelse(bp); +} + +pub fn iinit(dev: u32) { + unsafe { + readsb(dev, &mut SB); + println!( + "sb: size {} nblocks {} ninodes {} nlog {} logstart {} inodestart {} bmap start {}", + SB.size, SB.nblocks, SB.ninodes, SB.nlog, SB.logstart, SB.inodestart, SB.bmapstart + ); + } +} + +pub fn iget(dev: u32, inum: u32) -> usize { + unsafe { + let mut empty: Option = None; + + for i in 0..NINODE { + let ip = &mut ICACHE.inode[i]; + + if ip.refcnt > 0 && ip.dev == dev && ip.inum == inum { + ip.refcnt += 1; + return i; + } + + if empty.is_none() && ip.refcnt == 0 { + empty = Some(i); + } + } + + let idx = empty.unwrap_or_else(|| panic!("iget: no inodes")); + let ip = &mut ICACHE.inode[idx]; + + ip.dev = dev; + ip.inum = inum; + ip.refcnt = 1; + ip.valid = 0; + + idx + } +} + +pub fn iread(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iread: bad inode index"); + } + + let ip = &mut ICACHE.inode[idx]; + if ip.refcnt < 1 { + panic!("iread"); + } + + if ip.valid == 0 { + let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); + let data = &bio::buf_mut(bp).data; + + let off = (ip.inum % IPB) as usize * DINODE_SIZE; + ip.type_ = read_i16_le(data, off); + ip.major = read_i16_le(data, off + 2); + ip.minor = read_i16_le(data, off + 4); + ip.nlink = read_i16_le(data, off + 6); + ip.size = read_u32_le(data, off + 8); + + for i in 0..(NDIRECT + 1) { + ip.addrs[i] = read_u32_le(data, off + 12 + (i * 4)); + } + + bio::brelse(bp); + + ip.valid = 1; + if ip.type_ == 0 { + panic!("iread: no type"); + } + } + } +} + +fn bmap(ip: &Inode, bn: u32) -> u32 { + if (bn as usize) < NDIRECT { + return ip.addrs[bn as usize]; + } + + let bn = bn - (NDIRECT as u32); + if (bn as usize) < NINDIRECT { + let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); + let addr = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, (bn as usize) * 4) + }; + bio::brelse(bp); + return addr; + } + + panic!("bmap: out of range"); +} + +pub fn stati(idx: usize, st: &mut Stat) { + unsafe { + if idx >= NINODE { + panic!("stati: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + st.dev = ip.dev as i32; + st.ino = ip.inum; + st.type_ = ip.type_; + st.nlink = ip.nlink; + st.size = ip.size; + } +} + +pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { + unsafe { + if idx >= NINODE { + panic!("readi: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + if off > ip.size || off.checked_add(n).is_none() { + return -1; + } + + let mut n = n; + if off + n > ip.size { + n = ip.size - off; + } + + if (n as usize) > dst.len() { + panic!("readi: destination too small"); + } + + let mut tot: u32 = 0; + let mut cur_off = off; + + while tot < n { + let bp = bio::bread(ip.dev, bmap(ip, cur_off / (BSIZE as u32))); + + let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); + let boff = cur_off as usize % BSIZE; + dst[tot as usize..tot as usize + m] + .copy_from_slice(&bio::buf_mut(bp).data[boff..boff + m]); + bio::brelse(bp); + + tot += m as u32; + cur_off += m as u32; + } + + n as i32 + } +} \ No newline at end of file diff --git a/src/ide.rs b/src/ide.rs new file mode 100644 index 0000000..e7ac913 --- /dev/null +++ b/src/ide.rs @@ -0,0 +1,170 @@ +use core::sync::atomic::Ordering; +use crate::buf::{BSIZE, B_DIRTY, B_VALID}; +use crate::x86; + +const SECTOR_SIZE: usize = 512; + +const IDE_BSY: u8 = 0x80; +const IDE_DRDY: u8 = 0x40; +const IDE_DF: u8 = 0x20; +const IDE_ERR: u8 = 0x01; + +const IDE_CMD_READ: u8 = 0x20; +const IDE_CMD_WRITE: u8 = 0x30; +const IDE_CMD_RDMUL: u8 = 0xC4; +const IDE_CMD_WRMUL: u8 = 0xC5; + +// If you later build a real FS image, keep this consistent with your mkfs. +// xv6 uses 1000. +const FSSIZE: u32 = 1000; + +static mut IDEQUEUE: Option = None; +static mut HAVEDISK1: bool = false; + +fn idewait(checkerr: bool) -> i32 { + let mut r: u8; + loop { + r = x86::inb(0x1F7); + if (r & (IDE_BSY | IDE_DRDY)) == IDE_DRDY { + break; + } + } + if checkerr && (r & (IDE_DF | IDE_ERR)) != 0 { + return -1; + } + 0 +} + +pub fn ideinit() { + // Route IDE IRQ somewhere; simplest is CPU 0 for now. + crate::ioapic::ioapic_enable(crate::constants::IRQ_IDE, 0); + + idewait(false); + + // Check if disk 1 is present + unsafe { + x86::outb(0x1F6, 0xE0 | (1 << 4)); + for _ in 0..1000 { + if x86::inb(0x1F7) != 0 { + HAVEDISK1 = true; + break; + } + } + // Switch back to disk 0 + x86::outb(0x1F6, 0xE0 | (0 << 4)); + } +} + +fn idestart(idx: usize) { + let b = crate::bio::buf_mut(idx); + + if b.blockno >= FSSIZE { + panic!("idestart: incorrect blockno"); + } + + let sector_per_block = BSIZE / SECTOR_SIZE; // usually 1 + let sector = (b.blockno as usize) * sector_per_block; + + let read_cmd = if sector_per_block == 1 { IDE_CMD_READ } else { IDE_CMD_RDMUL }; + let write_cmd = if sector_per_block == 1 { IDE_CMD_WRITE } else { IDE_CMD_WRMUL }; + + if sector_per_block > 7 { + panic!("idestart: sector_per_block > 7"); + } + + idewait(false); + x86::outb(0x3F6, 0); // generate interrupt + + x86::outb(0x1F2, sector_per_block as u8); // number of sectors + x86::outb(0x1F3, (sector & 0xFF) as u8); + x86::outb(0x1F4, ((sector >> 8) & 0xFF) as u8); + x86::outb(0x1F5, ((sector >> 16) & 0xFF) as u8); + x86::outb( + 0x1F6, + 0xE0 | (((b.dev & 1) as u8) << 4) | (((sector >> 24) & 0x0F) as u8), + ); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & B_DIRTY) != 0 { + x86::outb(0x1F7, write_cmd); + + // write BSIZE bytes as u32 words + unsafe { + x86::outsl(0x1F0, b.data.as_ptr() as *const u32, BSIZE / 4); + } + } else { + x86::outb(0x1F7, read_cmd); + } +} + +// Interrupt handler. +pub fn ideintr() { + let idx = unsafe { + match IDEQUEUE { + None => return, + Some(i) => { + let b = crate::bio::buf_mut(i); + IDEQUEUE = b.qnext; + b.qnext = None; + i + } + } + }; + + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + + // Read data if needed. + if (flags & B_DIRTY) == 0 && idewait(true) >= 0 { + unsafe { + x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + + // Start next buffer in queue. + unsafe { + if let Some(next) = IDEQUEUE { + idestart(next); + } + } +} + +// ide.rs +pub fn iderw(idx: usize) { + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + panic!("iderw: nothing to do"); + } + + unsafe { + if b.dev != 0 && !HAVEDISK1 { + panic!("iderw: ide disk 1 not present"); + } + } + + // PURE POLLING: issue the command directly (no IDEQUEUE). + idestart(idx); + + // Wait for completion. + if idewait(true) < 0 { + panic!("iderw: ide error"); + } + + // If it was a read, pull data now. + let flags_now = b.flags.load(Ordering::Acquire); + if (flags_now & B_DIRTY) == 0 { + unsafe { + crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + // If it was a write, data was already pushed in idestart() via outsl(). + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa34772..4e67f12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,10 @@ mod mp; mod proc; mod traps; mod constants; - +mod buf; +mod bio; +mod ide; +mod fs; use crate::traps::*; #[macro_export] @@ -35,8 +38,61 @@ fn halt() -> ! { } } +fn print_bytes(bytes: &[u8]) { + for &ch in bytes { + console::consputc(ch as char); + } +} + +fn print_cstr(bytes: &[u8]) { + for &ch in bytes { + if ch == 0 { + break; + } + console::consputc(ch as char); + } +} + +fn welcome() { + const NDIR_READ: usize = 4; + + let root = fs::iget(param::ROOTDEV, fs::ROOTINO); + fs::iread(root); + + let mut raw_entries = [0u8; fs::DIRENT_SIZE * NDIR_READ]; + let entries_len = raw_entries.len() as u32; + let n = fs::readi(root, &mut raw_entries, 0, entries_len); + println!("Read {} bytes from inode of root directory", n); + + let mut entries = [fs::Dirent::new(); NDIR_READ]; + for i in 0..NDIR_READ { + let start = i * fs::DIRENT_SIZE; + let end = start + fs::DIRENT_SIZE; + entries[i] = fs::parse_dirent(&raw_entries[start..end]); + + print_bytes(b"name: "); + print_cstr(&entries[i].name); + println!(" is at inum: {}", entries[i].inum); + } + + let wtxt = fs::iget(param::ROOTDEV, entries[2].inum as u32); + fs::iread(wtxt); + let mut st = fs::Stat::new(); + fs::stati(wtxt, &mut st); + println!( + "\nwelcome.txt stats: Device {}, inode number {}, type {}, number of links {}, size {}", + st.dev, st.ino, st.type_, st.nlink, st.size + ); + + let mut greet = [0u8; 512]; + let n = fs::readi(wtxt, &mut greet, 0, st.size); + println!("Read {} bytes from welcome.txt", n); + print_bytes(&greet[..n as usize]); + console::consputc('\n'); +} + extern "C" { - pub static alltraps: fn(); + pub fn alltraps(); } #[no_mangle] @@ -46,9 +102,14 @@ pub extern "C" fn entryofrust() -> ! { picirq::picinit(); ioapic::ioapic_init(); uart::uartinit(); + ide::ideinit(); tvinit(); + bio::binit(); idtinit(); x86::sti(); + fs::iinit(param::ROOTDEV); + welcome(); + loop { x86::wfi(); } @@ -58,4 +119,4 @@ pub extern "C" fn entryofrust() -> ! { fn panic(info: &PanicInfo) -> ! { println!("Kernel Panic: {:?}", info); loop {} -} \ No newline at end of file +} diff --git a/src/param.rs b/src/param.rs index 4341329..63a19c7 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,2 +1,4 @@ pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack pub const NCPU: usize = 8; // maximum number of CPUs +pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const ROOTDEV: u32 = 1; // device number of file system root disk diff --git a/src/traps.rs b/src/traps.rs index 0643fcc..1278148 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -14,6 +14,7 @@ pub const T_IRQ0: u32 = 32; pub const IRQ_TIMER: u32 = 0; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; +const LOG_TICKS: bool = false; extern "C" { static vectors: [usize; 256]; // remove assembly. @@ -96,7 +97,7 @@ pub fn tvinit() { 0 // Descriptor privilege level. ); } - IDT.idt.set(arr); + let _ = IDT.idt.set(arr); } pub fn idtinit() { @@ -116,11 +117,12 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const TIMER: u32 = T_IRQ0 + IRQ_TIMER; const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; - match tf.trapno { TIMER => { *IDT.ticks.borrow_mut() += 1; - println!("Tick {}!", IDT.ticks.borrow()); + if LOG_TICKS { + println!("Tick {}!", IDT.ticks.borrow()); + } lapic::lapiceoi(); } SEVEN | SPURIOUS => { @@ -132,6 +134,11 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { ); lapiceoi(); } + crate::constants::IDE_TRAP => { + crate::ide::ideintr(); + crate::lapic::lapiceoi(); + } + _ => { println!( "unexpected trap {} from cpu {} eip {} (cr2=0x{:x})\n", @@ -143,4 +150,4 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { panic!("trap happened"); } } -} \ No newline at end of file +} diff --git a/src/uart.rs b/src/uart.rs index 9888088..a6c800b 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,12 @@ use crate::x86::{outb, inb}; +use crate::console::consoleintr; use crate::ioapic::ioapic_enable; +use core::sync::atomic::{AtomicBool, Ordering}; const COM1: u16 = 0x3F8; // COM1 port address pub const IRQ_COM1: u32 = 4; + +static UART_PRESENT: AtomicBool = AtomicBool::new(false); + pub fn uartinit() { // Turn off the FIFO. outb(COM1 + 2, 0); @@ -19,7 +24,9 @@ pub fn uartinit() { if inb(COM1 + 5) == 0xFF { return; } - + + UART_PRESENT.store(true, Ordering::Relaxed); + // Acknowledge pre-existing interrupt conditions; // enable interrupts. inb(COM1+2); @@ -33,10 +40,27 @@ pub fn uartinit() { } pub fn uartputc(c: char) { + if !UART_PRESENT.load(Ordering::Relaxed) { + return; + } for _ in 0..128 { if inb(COM1 + 5) & 0x20 != 0 { break; } } outb(COM1 + 0, c as u8); -} \ No newline at end of file +} + +fn uartgetc() -> i32 { + if !UART_PRESENT.load(Ordering::Relaxed) { + return -1; + } + if (inb(COM1 + 5) & 0x01) == 0 { + return -1; + } + inb(COM1 + 0) as i32 +} + +pub fn uartintr() { + consoleintr(uartgetc); +} diff --git a/src/x86.rs b/src/x86.rs index ba0598c..fb341ef 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -112,6 +112,39 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn noop() { + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); +} + +// x86.rs +pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { + core::arch::asm!( + "cld", + "rep insd", + in("dx") port, + inout("edi") (addr as usize) => _, + inout("ecx") cnt => _, + options(nostack, preserves_flags), + ); +} + + + +pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { + let mut p = addr; + for _ in 0..cnt { + let val = core::ptr::read(p); + asm!( + "out dx, eax", + in("dx") port, + in("eax") val, + options(nomem, nostack, preserves_flags), + ); + p = p.add(1); + } +} + + /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state diff --git a/welcome.txt b/welcome.txt new file mode 100644 index 0000000..5809c82 --- /dev/null +++ b/welcome.txt @@ -0,0 +1,7 @@ + ### + # # ###### # #### #### # # ###### ### + # # # # # # # # ## ## # ### + # # ##### # # # # # ## # ##### # + # ## # # # # # # # # # + ## ## # # # # # # # # # ### + # # ###### ###### #### #### # # ###### ### From dd458e3fa248bfb229553dfe90f5fab193dea118 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sat, 14 Feb 2026 17:53:19 +0530 Subject: [PATCH 06/69] remove rustc ICE dump file --- rustc-ice-2026-02-14T12_17_44-2018.txt | 50 -------------------------- 1 file changed, 50 deletions(-) delete mode 100644 rustc-ice-2026-02-14T12_17_44-2018.txt diff --git a/rustc-ice-2026-02-14T12_17_44-2018.txt b/rustc-ice-2026-02-14T12_17_44-2018.txt deleted file mode 100644 index 6569374..0000000 --- a/rustc-ice-2026-02-14T12_17_44-2018.txt +++ /dev/null @@ -1,50 +0,0 @@ -thread 'rustc' panicked at /rustc-dev/7057231bd78d6c7893f905ea1832365d4c5efe17/library/core/src/slice/index.rs:1027:55: -slice index starts at 54 but ends at 52 -stack backtrace: - 0: 0x72342475904b - ::create - 1: 0x723424758f95 - ::force_capture - 2: 0x7234237516d8 - std[bef1e3e6ef206a1]::panicking::update_hook::>::{closure#0} - 3: 0x72342476bb32 - std[bef1e3e6ef206a1]::panicking::panic_with_hook - 4: 0x72342474e558 - std[bef1e3e6ef206a1]::panicking::panic_handler::{closure#0} - 5: 0x723424742ac9 - std[bef1e3e6ef206a1]::sys::backtrace::__rust_end_short_backtrace:: - 6: 0x72342474ffbd - __rustc[7b32525fab805468]::rust_begin_unwind - 7: 0x723421694bac - core[14f954927b42fbe0]::panicking::panic_fmt - 8: 0x72342272a94d - core[14f954927b42fbe0]::slice::index::slice_index_fail - 9: 0x723423089065 - ::replace - 10: 0x72342612fab5 - annotate_snippets[f15529b311271195]::renderer::render::render - 11: 0x72342634881f - ::emit_messages_default - 12: 0x723426340feb - ::emit_diagnostic - 13: 0x72342633fe94 - ::from_errors_diagnostic - 14: 0x72342633f61d - ::emit_diagnostic - 15: 0x723426367b30 - ::emit_diagnostic::{closure#3} - 16: 0x723426365aa6 - rustc_interface[5d23d87c33b7100c]::callbacks::track_diagnostic::> - 17: 0x723426364b2e - ::emit_diagnostic - 18: 0x7234263649e1 - ::emit_diagnostic - 19: 0x723421291e8c - ::emit_producing_guarantee - 20: 0x7234216efa86 - rustc_borrowck[ab19dfe50827342]::borrowck_check_region_constraints - 21: 0x723425ea9eb3 - ::do_mir_borrowck - 22: 0x723425ea472f - rustc_borrowck[ab19dfe50827342]::mir_borrowck - 23: 0x723425ea4579 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::compute_fn::__rust_begin_short_backtrace - 24: 0x72342574507e - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::, rustc_query_system[50dced63bea56a4a]::dep_graph::graph::DepNodeIndex>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> - 25: 0x723425743ba3 - rustc_query_impl[19f3565069731b2f]::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace - 26: 0x723425743d65 - ::par_hir_body_owners::::{closure#0} - 27: 0x723425740c54 - rustc_interface[5d23d87c33b7100c]::passes::analysis - 28: 0x7234260504a6 - rustc_query_impl[19f3565069731b2f]::execution::try_execute_query::>, {rustc_query_impl[19f3565069731b2f]::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, false> - 29: 0x72342605027c - rustc_query_impl[19f3565069731b2f]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace - 30: 0x7234261f6be7 - , rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2}>::{closure#2} as core[14f954927b42fbe0]::ops::function::FnOnce<(&rustc_session[2f7fc1337f473eb7]::session::Session, rustc_middle[8b4cd4281f9facd9]::ty::context::CurrentGcx, alloc[97538fc0341e4f07]::sync::Arc, &std[bef1e3e6ef206a1]::sync::once_lock::OnceLock, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, &rustc_data_structures[5b3f8bc5aac74be7]::sync::worker_local::WorkerLocal, rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}::{closure#2})>>::call_once::{shim:vtable#0} - 31: 0x72342602e0b7 - rustc_interface[5d23d87c33b7100c]::interface::run_compiler::<(), rustc_driver_impl[f9ad7bf1bed37554]::run_compiler::{closure#0}>::{closure#1} - 32: 0x723426016dbe - std[bef1e3e6ef206a1]::sys::backtrace::__rust_begin_short_backtrace::::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()> - 33: 0x7234260172e0 - ::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[14f954927b42fbe0]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} - 34: 0x72342601816c - ::new::thread_start - 35: 0x72341f89caa4 - - 36: 0x72341f929c6c - - 37: 0x0 - - - -rustc version: 1.95.0-nightly (7057231bd 2026-02-11) -platform: x86_64-unknown-linux-gnu - -query stack during panic: -#0 [mir_borrowck] borrow-checking `welcome` -#1 [analysis] running analysis passes on crate `kernel` -end of query stack From 56d0f114ee7402908f00f8f9fe283e53a6e234ef Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sun, 15 Feb 2026 17:16:53 +0530 Subject: [PATCH 07/69] implement filesystem write path and directory/name operations --- src/fs.rs | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 65 +++++---- 2 files changed, 428 insertions(+), 40 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 8b6acaf..e934c01 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -8,8 +8,14 @@ use crate::println; pub const ROOTINO: u32 = 1; pub const NDIRECT: usize = 12; pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); +pub const MAXFILE: usize = NDIRECT + NINDIRECT; pub const DIRSIZ: usize = 14; pub const DIRENT_SIZE: usize = 2 + DIRSIZ; +pub const BPB: u32 = (BSIZE * 8) as u32; + +pub const T_DIR: i16 = 1; +pub const T_FILE: i16 = 2; +pub const T_DEV: i16 = 3; const DINODE_SIZE: usize = 2 + 2 + 2 + 2 + 4 + ((NDIRECT + 1) * 4); const IPB: u32 = (BSIZE / DINODE_SIZE) as u32; @@ -139,11 +145,40 @@ fn read_u32_le(data: &[u8], off: usize) -> u32 { u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) } +#[inline] +fn write_u16_le(data: &mut [u8], off: usize, val: u16) { + data[off..off + 2].copy_from_slice(&val.to_le_bytes()); +} + +#[inline] +fn write_i16_le(data: &mut [u8], off: usize, val: i16) { + data[off..off + 2].copy_from_slice(&val.to_le_bytes()); +} + +#[inline] +fn write_u32_le(data: &mut [u8], off: usize, val: u32) { + data[off..off + 4].copy_from_slice(&val.to_le_bytes()); +} + #[inline] fn iblock(inum: u32, sb: &Superblock) -> u32 { inum / IPB + sb.inodestart } +#[inline] +fn bblock(b: u32, sb: &Superblock) -> u32 { + b / BPB + sb.bmapstart +} + +#[inline] +fn name_to_dirsiz(name: &str) -> [u8; DIRSIZ] { + let mut out = [0u8; DIRSIZ]; + let bytes = name.as_bytes(); + let n = min(bytes.len(), DIRSIZ); + out[..n].copy_from_slice(&bytes[..n]); + out +} + pub fn parse_dirent(raw: &[u8]) -> Dirent { let mut de = Dirent::new(); de.inum = read_u16_le(raw, 0); @@ -176,6 +211,119 @@ pub fn iinit(dev: u32) { } } +fn bzero(dev: u32, bno: u32) { + let bp = bio::bread(dev, bno); + bio::buf_mut(bp).data.fill(0); + bio::bwrite(bp); + bio::brelse(bp); +} + +fn balloc(dev: u32) -> u32 { + unsafe { + let mut b = 0u32; + while b < SB.size { + let bp = bio::bread(dev, bblock(b, &SB)); + let mut found: Option = None; + { + let data = &mut bio::buf_mut(bp).data; + let mut bi = 0u32; + while bi < BPB && b + bi < SB.size { + let m: u8 = 1u8 << (bi % 8); + let idx = (bi / 8) as usize; + if (data[idx] & m) == 0 { + data[idx] |= m; + found = Some(b + bi); + break; + } + bi += 1; + } + } + + if let Some(blockno) = found { + bio::bwrite(bp); + bio::brelse(bp); + bzero(dev, blockno); + return blockno; + } + + bio::brelse(bp); + b += BPB; + } + } + + panic!("balloc: out of blocks"); +} + +pub fn ialloc(dev: u32, type_: i16) -> usize { + unsafe { + let mut inum = 1u32; + while inum < SB.ninodes { + let bp = bio::bread(dev, iblock(inum, &SB)); + let off = (inum % IPB) as usize * DINODE_SIZE; + + let free = { + let data = &bio::buf_mut(bp).data; + read_i16_le(data, off) == 0 + }; + + if free { + { + let data = &mut bio::buf_mut(bp).data; + data[off..off + DINODE_SIZE].fill(0); + write_i16_le(data, off, type_); + } + bio::bwrite(bp); + bio::brelse(bp); + return iget(dev, inum); + } + + bio::brelse(bp); + inum += 1; + } + } + + panic!("ialloc: no inodes"); +} + +pub fn irelease(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("irelease: bad inode index"); + } + if ICACHE.inode[idx].refcnt < 1 { + panic!("irelease: ref underflow"); + } + ICACHE.inode[idx].refcnt -= 1; + } +} + +pub fn iupdate(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iupdate: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); + let off = (ip.inum % IPB) as usize * DINODE_SIZE; + + { + let data = &mut bio::buf_mut(bp).data; + write_i16_le(data, off, ip.type_); + write_i16_le(data, off + 2, ip.major); + write_i16_le(data, off + 4, ip.minor); + write_i16_le(data, off + 6, ip.nlink); + write_u32_le(data, off + 8, ip.size); + for i in 0..(NDIRECT + 1) { + write_u32_le(data, off + 12 + (i * 4), ip.addrs[i]); + } + } + + bio::bwrite(bp); + bio::brelse(bp); + } +} + pub fn iget(dev: u32, inum: u32) -> usize { unsafe { let mut empty: Option = None; @@ -241,20 +389,44 @@ pub fn iread(idx: usize) { } } -fn bmap(ip: &Inode, bn: u32) -> u32 { - if (bn as usize) < NDIRECT { - return ip.addrs[bn as usize]; - } +fn bmap(idx: usize, bn: u32) -> u32 { + unsafe { + if idx >= NINODE { + panic!("bmap: bad inode index"); + } - let bn = bn - (NDIRECT as u32); - if (bn as usize) < NINDIRECT { - let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); - let addr = { - let data = &bio::buf_mut(bp).data; - read_u32_le(data, (bn as usize) * 4) - }; - bio::brelse(bp); - return addr; + let ip = &mut ICACHE.inode[idx]; + + if (bn as usize) < NDIRECT { + let direct = &mut ip.addrs[bn as usize]; + if *direct == 0 { + *direct = balloc(ip.dev); + } + return *direct; + } + + let bn = bn - (NDIRECT as u32); + if (bn as usize) < NINDIRECT { + if ip.addrs[NDIRECT] == 0 { + ip.addrs[NDIRECT] = balloc(ip.dev); + } + + let bp = bio::bread(ip.dev, ip.addrs[NDIRECT]); + let mut addr = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, (bn as usize) * 4) + }; + if addr == 0 { + addr = balloc(ip.dev); + { + let data = &mut bio::buf_mut(bp).data; + write_u32_le(data, (bn as usize) * 4, addr); + } + bio::bwrite(bp); + } + bio::brelse(bp); + return addr; + } } panic!("bmap: out of range"); @@ -297,9 +469,10 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { let mut tot: u32 = 0; let mut cur_off = off; + let dev = ip.dev; while tot < n { - let bp = bio::bread(ip.dev, bmap(ip, cur_off / (BSIZE as u32))); + let bp = bio::bread(dev, bmap(idx, cur_off / (BSIZE as u32))); let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); let boff = cur_off as usize % BSIZE; @@ -313,4 +486,204 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { n as i32 } -} \ No newline at end of file +} + +pub fn writei(idx: usize, src: &[u8], off: u32, n: u32) -> i32 { + unsafe { + if idx >= NINODE { + panic!("writei: bad inode index"); + } + + let ip = &ICACHE.inode[idx]; + if off > ip.size || off.checked_add(n).is_none() { + return -1; + } + if off + n > (MAXFILE * BSIZE) as u32 { + return -1; + } + if (n as usize) > src.len() { + panic!("writei: source too small"); + } + + let dev = ip.dev; + let mut tot: u32 = 0; + let mut cur_off = off; + + while tot < n { + let bp = bio::bread(dev, bmap(idx, cur_off / (BSIZE as u32))); + let m = min((n - tot) as usize, BSIZE - (cur_off as usize % BSIZE)); + let boff = cur_off as usize % BSIZE; + bio::buf_mut(bp).data[boff..boff + m] + .copy_from_slice(&src[tot as usize..tot as usize + m]); + bio::bwrite(bp); + bio::brelse(bp); + + tot += m as u32; + cur_off += m as u32; + } + + if n > 0 && cur_off > ICACHE.inode[idx].size { + ICACHE.inode[idx].size = cur_off; + iupdate(idx); + } + + n as i32 + } +} + +pub fn inode_inum(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_inum: bad inode index"); + } + ICACHE.inode[idx].inum + } +} + +pub fn namecmp(s: &str, t: &[u8; DIRSIZ]) -> bool { + name_to_dirsiz(s) == *t +} + +pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Option { + unsafe { + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); + } + } + iread(dp_idx); + + unsafe { + let dp = &ICACHE.inode[dp_idx]; + if dp.type_ != T_DIR { + panic!("dirlookup not DIR"); + } + + let mut off = 0u32; + while off < dp.size { + let mut raw = [0u8; DIRENT_SIZE]; + if readi(dp_idx, &mut raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlookup read"); + } + let de = parse_dirent(&raw); + if de.inum != 0 && namecmp(name, &de.name) { + if let Some(ref mut out_off) = poff { + **out_off = off; + } + return Some(iget(dp.dev, de.inum as u32)); + } + off += DIRENT_SIZE as u32; + } + } + + None +} + +pub fn dirlink(dp_idx: usize, name: &str, inum: u32) -> i32 { + if let Some(ip_idx) = dirlookup(dp_idx, name, None) { + irelease(ip_idx); + return -1; + } + + let mut off = 0u32; + unsafe { + if dp_idx >= NINODE { + panic!("dirlink: bad inode index"); + } + while off < ICACHE.inode[dp_idx].size { + let mut raw = [0u8; DIRENT_SIZE]; + if readi(dp_idx, &mut raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlink read"); + } + let de = parse_dirent(&raw); + if de.inum == 0 { + break; + } + off += DIRENT_SIZE as u32; + } + } + + let mut raw = [0u8; DIRENT_SIZE]; + write_u16_le(&mut raw, 0, inum as u16); + raw[2..2 + DIRSIZ].copy_from_slice(&name_to_dirsiz(name)); + if writei(dp_idx, &raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { + panic!("dirlink"); + } + + 0 +} + +fn skipelem(path: &[u8], mut i: usize, name: &mut [u8; DIRSIZ]) -> Option { + while i < path.len() && path[i] == b'/' { + i += 1; + } + if i == path.len() { + return None; + } + + let start = i; + while i < path.len() && path[i] != b'/' { + i += 1; + } + let len = i - start; + + name.fill(0); + if len >= DIRSIZ { + name.copy_from_slice(&path[start..start + DIRSIZ]); + } else { + name[..len].copy_from_slice(&path[start..i]); + } + + while i < path.len() && path[i] == b'/' { + i += 1; + } + Some(i) +} + +fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option { + let bytes = path.as_bytes(); + let mut path_idx = 0usize; + let mut ip = iget(crate::param::ROOTDEV, ROOTINO); + + while let Some(next_idx) = skipelem(bytes, path_idx, name) { + path_idx = next_idx; + iread(ip); + + unsafe { + if ICACHE.inode[ip].type_ != T_DIR { + irelease(ip); + return None; + } + } + + if nameiparent && path_idx == bytes.len() { + return Some(ip); + } + + let elem = core::str::from_utf8(name).unwrap_or(""); + let trimmed = elem.trim_end_matches('\0'); + let next = match dirlookup(ip, trimmed, None) { + Some(x) => x, + None => { + irelease(ip); + return None; + } + }; + irelease(ip); + ip = next; + } + + if nameiparent { + irelease(ip); + return None; + } + Some(ip) +} + +pub fn namei(path: &str) -> Option { + let mut name = [0u8; DIRSIZ]; + namex(path, false, &mut name) +} + +pub fn nameiparent(path: &str, name: &mut [u8; DIRSIZ]) -> Option { + namex(path, true, name) +} diff --git a/src/lib.rs b/src/lib.rs index 4e67f12..42b72f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,41 +54,56 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - const NDIR_READ: usize = 4; - - let root = fs::iget(param::ROOTDEV, fs::ROOTINO); + let root = fs::namei("/").unwrap_or_else(|| panic!("root not found")); fs::iread(root); + let foodir = match fs::dirlookup(root, "foo", None) { + Some(idx) => idx, + None => { + println!("/foo not found. Creating!"); + let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); + fs::iread(idx); + let ino = fs::inode_inum(idx); + if fs::dirlink(idx, ".", ino) < 0 { + panic!("failed to link . in /foo"); + } + if fs::dirlink(idx, "..", ino) < 0 { + panic!("failed to link .. in /foo"); + } + if fs::dirlink(root, "foo", ino) < 0 { + panic!("failed to link /foo in root"); + } + idx + } + }; + + let wtxt = match fs::namei("/foo/greet.txt") { + Some(idx) => idx, + None => { + println!("/foo/greet.txt not found. Creating!"); + let wtxt_orig = + fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); + let inum = fs::inode_inum(wtxt_orig); + if fs::dirlink(foodir, "greet.txt", inum) < 0 { + panic!("failed to link greet.txt in /foo"); + } + fs::irelease(wtxt_orig); + fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) + } + }; - let mut raw_entries = [0u8; fs::DIRENT_SIZE * NDIR_READ]; - let entries_len = raw_entries.len() as u32; - let n = fs::readi(root, &mut raw_entries, 0, entries_len); - println!("Read {} bytes from inode of root directory", n); - - let mut entries = [fs::Dirent::new(); NDIR_READ]; - for i in 0..NDIR_READ { - let start = i * fs::DIRENT_SIZE; - let end = start + fs::DIRENT_SIZE; - entries[i] = fs::parse_dirent(&raw_entries[start..end]); - - print_bytes(b"name: "); - print_cstr(&entries[i].name); - println!(" is at inum: {}", entries[i].inum); - } - - let wtxt = fs::iget(param::ROOTDEV, entries[2].inum as u32); fs::iread(wtxt); let mut st = fs::Stat::new(); fs::stati(wtxt, &mut st); - println!( - "\nwelcome.txt stats: Device {}, inode number {}, type {}, number of links {}, size {}", - st.dev, st.ino, st.type_, st.nlink, st.size - ); let mut greet = [0u8; 512]; let n = fs::readi(wtxt, &mut greet, 0, st.size); - println!("Read {} bytes from welcome.txt", n); + println!("Read {} bytes from /foo/greet.txt", n); print_bytes(&greet[..n as usize]); console::consputc('\n'); + + fs::irelease(wtxt); + fs::irelease(foodir); + fs::irelease(root); } extern "C" { From 401a6162a406f8642ee75ef01f02977310e05be2 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Sun, 15 Feb 2026 17:33:28 +0530 Subject: [PATCH 08/69] Added p12-logs --- src/fs.rs | 13 ++-- src/lib.rs | 6 ++ src/log.rs | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/param.rs | 2 + 4 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 src/log.rs diff --git a/src/fs.rs b/src/fs.rs index e934c01..b0f03ee 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,6 +2,7 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; +use crate::log; use crate::param::NINODE; use crate::println; @@ -214,7 +215,7 @@ pub fn iinit(dev: u32) { fn bzero(dev: u32, bno: u32) { let bp = bio::bread(dev, bno); bio::buf_mut(bp).data.fill(0); - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); } @@ -240,7 +241,7 @@ fn balloc(dev: u32) -> u32 { } if let Some(blockno) = found { - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); bzero(dev, blockno); return blockno; @@ -272,7 +273,7 @@ pub fn ialloc(dev: u32, type_: i16) -> usize { data[off..off + DINODE_SIZE].fill(0); write_i16_le(data, off, type_); } - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); return iget(dev, inum); } @@ -319,7 +320,7 @@ pub fn iupdate(idx: usize) { } } - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); } } @@ -422,7 +423,7 @@ fn bmap(idx: usize, bn: u32) -> u32 { let data = &mut bio::buf_mut(bp).data; write_u32_le(data, (bn as usize) * 4, addr); } - bio::bwrite(bp); + log::log_write(bp); } bio::brelse(bp); return addr; @@ -515,7 +516,7 @@ pub fn writei(idx: usize, src: &[u8], off: u32, n: u32) -> i32 { let boff = cur_off as usize % BSIZE; bio::buf_mut(bp).data[boff..boff + m] .copy_from_slice(&src[tot as usize..tot as usize + m]); - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); tot += m as u32; diff --git a/src/lib.rs b/src/lib.rs index 42b72f5..1199920 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ mod buf; mod bio; mod ide; mod fs; +mod log; use crate::traps::*; #[macro_export] @@ -59,6 +60,7 @@ fn welcome() { let foodir = match fs::dirlookup(root, "foo", None) { Some(idx) => idx, None => { + log::begin_op(); println!("/foo not found. Creating!"); let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); fs::iread(idx); @@ -72,6 +74,7 @@ fn welcome() { if fs::dirlink(root, "foo", ino) < 0 { panic!("failed to link /foo in root"); } + log::end_op(); idx } }; @@ -79,6 +82,7 @@ fn welcome() { let wtxt = match fs::namei("/foo/greet.txt") { Some(idx) => idx, None => { + log::begin_op(); println!("/foo/greet.txt not found. Creating!"); let wtxt_orig = fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); @@ -87,6 +91,7 @@ fn welcome() { panic!("failed to link greet.txt in /foo"); } fs::irelease(wtxt_orig); + log::end_op(); fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) } }; @@ -123,6 +128,7 @@ pub extern "C" fn entryofrust() -> ! { idtinit(); x86::sti(); fs::iinit(param::ROOTDEV); + log::initlog(param::ROOTDEV); welcome(); loop { diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..36bd892 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,189 @@ +use core::mem::size_of; +use core::sync::atomic::Ordering; + +use crate::bio; +use crate::buf::{B_DIRTY, BSIZE}; +use crate::fs; +use crate::param::LOGSIZE; + +#[derive(Copy, Clone)] +struct LogHeader { + n: i32, + block: [u32; LOGSIZE], +} + +impl LogHeader { + const fn new() -> Self { + Self { + n: 0, + block: [0; LOGSIZE], + } + } +} + +struct Log { + start: u32, + size: u32, + committing: bool, + dev: u32, + lh: LogHeader, +} + +impl Log { + const fn new() -> Self { + Self { + start: 0, + size: 0, + committing: false, + dev: 0, + lh: LogHeader::new(), + } + } +} + +static mut LOG: Log = Log::new(); + +#[inline] +fn read_i32_le(data: &[u8], off: usize) -> i32 { + i32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) +} + +#[inline] +fn write_i32_le(data: &mut [u8], off: usize, val: i32) { + data[off..off + 4].copy_from_slice(&val.to_le_bytes()); +} + +#[inline] +fn read_u32_le(data: &[u8], off: usize) -> u32 { + u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]) +} + +#[inline] +fn write_u32_le(data: &mut [u8], off: usize, val: u32) { + data[off..off + 4].copy_from_slice(&val.to_le_bytes()); +} + +fn read_head() { + unsafe { + let buf = bio::bread(LOG.dev, LOG.start); + let data = &bio::buf_mut(buf).data; + LOG.lh.n = read_i32_le(data, 0); + for i in 0..(LOG.lh.n as usize) { + LOG.lh.block[i] = read_u32_le(data, 4 + i * 4); + } + bio::brelse(buf); + } +} + +fn write_head() { + unsafe { + let buf = bio::bread(LOG.dev, LOG.start); + let data = &mut bio::buf_mut(buf).data; + data.fill(0); + write_i32_le(data, 0, LOG.lh.n); + for i in 0..(LOG.lh.n as usize) { + write_u32_le(data, 4 + i * 4, LOG.lh.block[i]); + } + bio::bwrite(buf); + bio::brelse(buf); + } +} + +fn install_trans() { + unsafe { + for tail in 0..(LOG.lh.n as usize) { + let lbuf = bio::bread(LOG.dev, LOG.start + tail as u32 + 1); + let dbuf = bio::bread(LOG.dev, LOG.lh.block[tail]); + let src = bio::buf_mut(lbuf).data; + bio::buf_mut(dbuf).data.copy_from_slice(&src); + bio::bwrite(dbuf); + bio::brelse(lbuf); + bio::brelse(dbuf); + } + } +} + +fn recover_from_log() { + read_head(); + install_trans(); + unsafe { + LOG.lh.n = 0; + } + write_head(); +} + +fn write_log() { + unsafe { + for tail in 0..(LOG.lh.n as usize) { + let to = bio::bread(LOG.dev, LOG.start + tail as u32 + 1); + let from = bio::bread(LOG.dev, LOG.lh.block[tail]); + let src = bio::buf_mut(from).data; + bio::buf_mut(to).data.copy_from_slice(&src); + bio::bwrite(to); + bio::brelse(from); + bio::brelse(to); + } + } +} + +fn commit() { + unsafe { + if LOG.committing { + panic!("commit"); + } + LOG.committing = true; + if LOG.lh.n > 0 { + write_log(); + write_head(); + install_trans(); + LOG.lh.n = 0; + write_head(); + } + LOG.committing = false; + } +} + +pub fn initlog(dev: u32) { + if size_of::() >= BSIZE { + panic!("initlog: too big logheader"); + } + + let mut sb = fs::Superblock::new(); + fs::readsb(dev, &mut sb); + unsafe { + LOG.start = sb.logstart; + LOG.size = sb.nlog; + LOG.dev = dev; + } + recover_from_log(); +} + +pub fn begin_op() {} + +pub fn end_op() { + commit(); +} + +pub fn log_write(idx: usize) { + unsafe { + let blockno = bio::buf_mut(idx).blockno; + if LOG.lh.n as usize >= LOGSIZE || LOG.lh.n as u32 >= LOG.size.saturating_sub(1) { + panic!("too big a transaction"); + } + + let mut i = 0usize; + while i < LOG.lh.n as usize { + if LOG.lh.block[i] == blockno { + break; + } + i += 1; + } + + LOG.lh.block[i] = blockno; + if i == LOG.lh.n as usize { + LOG.lh.n += 1; + } + + bio::buf_mut(idx).flags.fetch_or(B_DIRTY, Ordering::AcqRel); + } +} diff --git a/src/param.rs b/src/param.rs index 63a19c7..3275c3b 100644 --- a/src/param.rs +++ b/src/param.rs @@ -2,3 +2,5 @@ pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack pub const NCPU: usize = 8; // maximum number of CPUs pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log From 5e2d7e19d3e0e3590a0d1f32a2ad4417f5a54944 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sun, 15 Feb 2026 17:28:24 +0000 Subject: [PATCH 09/69] Refactor traps module: centralize constants, remove duplicates, decouple ticks from IDT --- .cargo/config.toml | 6 ++++++ Makefile | 2 +- rust-toolchain.toml | 1 + src/lapic.rs | 2 +- src/traps.rs | 49 +++++++++------------------------------------ 5 files changed, 18 insertions(+), 42 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0953166 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "targets/i686.json" + +[unstable] +build-std = ["core"] +build-std-features = ["compiler-builtins-mem"] diff --git a/Makefile b/Makefile index fa7b33b..56e0958 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ bootblock: bootasm.S bootmain.c ./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 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5d56faf..87b5402 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] channel = "nightly" +components = ["rust-src"] \ No newline at end of file diff --git a/src/lapic.rs b/src/lapic.rs index 1ce725f..14f76e6 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -1,6 +1,6 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::traps::{T_IRQ0, IRQ_TIMER, IRQ_SPURIOUS, IRQ_ERROR}; +use crate::constants::{T_IRQ0, IRQ_TIMER, IRQ_ERROR, IRQ_SPURIOUS}; const ID: isize = 0x0020 / 4; const VER: isize = 0x0030 / 4; diff --git a/src/traps.rs b/src/traps.rs index 0643fcc..b593740 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -3,17 +3,9 @@ use core::cell::{OnceCell, RefCell}; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; -use crate::x86::{lidt,rcr2}; +use crate::x86::{lidt, rcr2, TrapFrame}; use crate::lapic; - -const SEG_KCODE: u16 = 1; -const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate -const STS_TG32: u8 = 0xF; // 32-bit Trap Gate - -pub const T_IRQ0: u32 = 32; -pub const IRQ_TIMER: u32 = 0; -pub const IRQ_ERROR: u32 = 19; -pub const IRQ_SPURIOUS: u32 = 31; +use crate::constants::{SEG_KCODE, STS_IG32, STS_TG32, T_IRQ0, IRQ_TIMER, IRQ_ERROR, IRQ_SPURIOUS}; extern "C" { static vectors: [usize; 256]; // remove assembly. @@ -52,38 +44,15 @@ impl GateDesc { #[repr(C)] pub struct IDTOnce { pub idt: OnceCell<[GateDesc; 256]>, - pub ticks: RefCell } unsafe impl Sync for IDTOnce {} -pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new(), ticks: RefCell::new(0) }; - - -#[repr(C)] -pub struct TrapFrame { - // registers as pushed by pusha - pub edi: u32, - pub esi: u32, - pub ebp: u32, - pub oesp: u32, // useless & ignored - pub ebx: u32, - pub edx: u32, - pub ecx: u32, - pub eax: u32, - - pub trapno: u32, - - // below here defined by x86 hardware - pub err: u32, - pub eip: u32, - pub cs: u16, - pub padding5: u16, - pub eflags: u32, +pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new() }; - // below here only when crossing rings, such as from user to kernel - pub esp: u32, - pub ss: u16, - pub padding6: u16, +struct TickCounter { + ticks: RefCell } +unsafe impl Sync for TickCounter {} +static TICKS: TickCounter = TickCounter { ticks: RefCell::new(0) }; pub fn tvinit() { @@ -119,8 +88,8 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { match tf.trapno { TIMER => { - *IDT.ticks.borrow_mut() += 1; - println!("Tick {}!", IDT.ticks.borrow()); + *TICKS.ticks.borrow_mut() += 1; + println!("Tick {}!", TICKS.ticks.borrow()); lapic::lapiceoi(); } SEVEN | SPURIOUS => { From 416b5f33b59abb1d622d90292626f1d97dc23d89 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sun, 15 Feb 2026 21:29:02 +0000 Subject: [PATCH 10/69] Updated TickCounter struct to pub --- src/traps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traps.rs b/src/traps.rs index b593740..aa1c1a3 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -48,7 +48,7 @@ pub struct IDTOnce { unsafe impl Sync for IDTOnce {} pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new() }; -struct TickCounter { +pub struct TickCounter { ticks: RefCell } unsafe impl Sync for TickCounter {} From f7a426cd8f096d55af989d72475761a94faae897 Mon Sep 17 00:00:00 2001 From: legends1307 Date: Mon, 16 Feb 2026 22:29:33 +0530 Subject: [PATCH 11/69] p9: add name layer + self-contained Rust mkfs - Added src/mkfs.rs: pure Rust mkfs tool (based on sudoheckbeluga's work in PR #5), adapted with FSSIZE=1000 and LOGSIZE=0 to match the C repo's p9-name-layer branch. No more ../col331/ dependency. - Updated Makefile: mkfs now compiled with rustc from src/mkfs.rs - Added name layer functions in src/fs.rs: irelse, namecmp, dirlookup, skipelem, namex, namei, nameiparent - Updated welcome() in src/lib.rs to use namei("/welcome.txt") --- Makefile | 8 +- src/fs.rs | 190 ++++++++++++++++++++++++++++ src/lib.rs | 27 +--- src/mkfs.rs | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+), 26 deletions(-) create mode 100644 src/mkfs.rs diff --git a/Makefile b/Makefile index 0df672e..e0a4253 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ RS = src/*.rs # Cross-compiling (e.g., on Mac OS X) #TOOLPREFIX = i386-jos-elf -#TOOLPREFIX = i386-elf- +# TOOLPREFIX = i686-elf- # Using native tools (e.g., on X86 Linux) #TOOLPREFIX = @@ -69,8 +69,8 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col331/param.h - gcc -Werror -Wall -o mkfs ../col331/mkfs.c +mkfs: src/mkfs.rs + rustc -W warnings -o mkfs src/mkfs.rs fs.img: mkfs welcome.txt ./mkfs fs.img welcome.txt @@ -87,7 +87,7 @@ kernel.a: $(RS) 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 diff --git a/src/fs.rs b/src/fs.rs index 8b6acaf..62fb447 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -11,6 +11,10 @@ pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); pub const DIRSIZ: usize = 14; pub const DIRENT_SIZE: usize = 2 + DIRSIZ; +pub const T_DIR: i16 = 1; // Directory +pub const T_FILE: i16 = 2; // File +pub const T_DEV: i16 = 3; // Device + const DINODE_SIZE: usize = 2 + 2 + 2 + 2 + 4 + ((NDIRECT + 1) * 4); const IPB: u32 = (BSIZE / DINODE_SIZE) as u32; @@ -176,6 +180,17 @@ pub fn iinit(dev: u32) { } } +/// Decrement reference count of inode at index idx. +/// Mirrors C: static void irelse(struct inode *ip) { ip->ref--; } +fn irelse(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("irelse: bad inode index"); + } + ICACHE.inode[idx].refcnt -= 1; + } +} + pub fn iget(dev: u32, inum: u32) -> usize { unsafe { let mut empty: Option = None; @@ -313,4 +328,179 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { n as i32 } +} + +// ===================================================================== +// Directories +// ===================================================================== + +/// Compare two directory entry names (up to DIRSIZ bytes). +/// Mirrors C: int namecmp(const char *s, const char *t) { return strncmp(s, t, DIRSIZ); } +pub fn namecmp(s: &[u8], t: &[u8]) -> bool { + let slen = s.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(s.len())); + let tlen = t.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(t.len())); + if slen != tlen { + return false; + } + for i in 0..slen { + if s[i] != t[i] { + return false; + } + } + true +} + +/// Look for a directory entry in a directory inode. +/// If found, return the inode cache index of the entry. +/// Mirrors C: struct inode* dirlookup(struct inode *dp, char *name, uint *poff) +pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { + unsafe { + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); + } + + let dp = &ICACHE.inode[dp_idx]; + if dp.type_ != T_DIR { + panic!("dirlookup not DIR"); + } + + let mut off: u32 = 0; + let de_size = DIRENT_SIZE as u32; + + while off < dp.size { + let mut raw = [0u8; DIRENT_SIZE]; + if readi(dp_idx, &mut raw, off, de_size) != de_size as i32 { + panic!("dirlookup read"); + } + + let de = parse_dirent(&raw); + if de.inum == 0 { + off += de_size; + continue; + } + + if namecmp(name, &de.name) { + // entry matches path element + let inum = de.inum as u32; + return Some(iget(dp.dev, inum)); + } + off += de_size; + } + + None + } +} + +// ===================================================================== +// Paths +// ===================================================================== + +/// Copy the next path element from path into name. +/// Return the remaining path after the element (with leading slashes stripped), +/// or None if there is no element to extract. +/// +/// Mirrors C: +/// skipelem("a/bb/c", name) = "bb/c", setting name = "a" +/// skipelem("///a//bb", name) = "bb", setting name = "a" +/// skipelem("a", name) = "", setting name = "a" +/// skipelem("", name) = skipelem("////", name) = 0 +fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { + let mut i = 0; + + // Skip leading slashes + while i < path.len() && path[i] == b'/' { + i += 1; + } + if i >= path.len() || path[i] == 0 { + return None; + } + + let start = i; + // Find end of this path element + while i < path.len() && path[i] != b'/' && path[i] != 0 { + i += 1; + } + let len = i - start; + + // Copy element into name + if len >= DIRSIZ { + name.copy_from_slice(&path[start..start + DIRSIZ]); + } else { + name[..len].copy_from_slice(&path[start..start + len]); + name[len] = 0; + // Zero out rest of name for cleanliness + for j in (len + 1)..DIRSIZ { + name[j] = 0; + } + } + + // Skip trailing slashes + while i < path.len() && path[i] == b'/' { + i += 1; + } + + Some(&path[i..]) +} + +/// Look up and return the inode cache index for a path name. +/// If nameiparent_flag is true, return the inode for the parent and copy +/// the final path element into name. +/// +/// Mirrors C: static struct inode* namex(char *path, int nameiparent, char *name) +fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option { + let mut ip = iget(crate::param::ROOTDEV, ROOTINO); + + let mut remaining = path; + loop { + match skipelem(remaining, name) { + None => break, + Some(rest) => { + iread(ip); + unsafe { + if ICACHE.inode[ip].type_ != T_DIR { + irelse(ip); + return None; + } + } + + // If looking for parent and this is the last element + if nameiparent_flag && (rest.is_empty() || rest[0] == 0) { + // Stop one level early. + return Some(ip); + } + + match dirlookup(ip, name) { + None => { + irelse(ip); + return None; + } + Some(next) => { + irelse(ip); + ip = next; + } + } + remaining = rest; + } + } + } + + if nameiparent_flag { + irelse(ip); + return None; + } + Some(ip) +} + +/// Look up the inode cache index for a path name. +/// Mirrors C: struct inode* namei(char *path) +pub fn namei(path: &[u8]) -> Option { + let mut name = [0u8; DIRSIZ]; + namex(path, false, &mut name) +} + +/// Look up the parent inode for a path name, and copy the final +/// path element into name. +/// Mirrors C: struct inode* nameiparent(char *path, char *name) +pub fn nameiparent(path: &[u8], name: &mut [u8; DIRSIZ]) -> Option { + namex(path, true, name) } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 4e67f12..ecf6b6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,28 +54,11 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - const NDIR_READ: usize = 4; - - let root = fs::iget(param::ROOTDEV, fs::ROOTINO); - fs::iread(root); - - let mut raw_entries = [0u8; fs::DIRENT_SIZE * NDIR_READ]; - let entries_len = raw_entries.len() as u32; - let n = fs::readi(root, &mut raw_entries, 0, entries_len); - println!("Read {} bytes from inode of root directory", n); - - let mut entries = [fs::Dirent::new(); NDIR_READ]; - for i in 0..NDIR_READ { - let start = i * fs::DIRENT_SIZE; - let end = start + fs::DIRENT_SIZE; - entries[i] = fs::parse_dirent(&raw_entries[start..end]); - - print_bytes(b"name: "); - print_cstr(&entries[i].name); - println!(" is at inum: {}", entries[i].inum); - } - - let wtxt = fs::iget(param::ROOTDEV, entries[2].inum as u32); + // p9: Use namei to look up /welcome.txt by path instead of manually + // reading directory entries from the root inode. + let wtxt = fs::namei(b"/welcome.txt").unwrap_or_else(|| { + panic!("welcome: /welcome.txt not found"); + }); fs::iread(wtxt); let mut st = fs::Stat::new(); fs::stati(wtxt, &mut st); diff --git a/src/mkfs.rs b/src/mkfs.rs new file mode 100644 index 0000000..6363752 --- /dev/null +++ b/src/mkfs.rs @@ -0,0 +1,358 @@ +use std::env; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::mem::size_of; +use std::path::Path; + +// File system constants — must match param.h / fs.h from C repo p9-name-layer +const BSIZE: usize = 512; +const FSSIZE: u32 = 1000; +const NINODES: u32 = 200; +const LOGSIZE: u32 = 0; + +const ROOTINO: u32 = 1; +const NDIRECT: usize = 12; +const NINDIRECT: usize = BSIZE / size_of::(); +const MAXFILE: usize = NDIRECT + NINDIRECT; +const DIRSIZ: usize = 14; + +const T_DIR: u16 = 1; +const T_FILE: u16 = 2; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct Superblock { + size: u32, + nblocks: u32, + ninodes: u32, + nlog: u32, + logstart: u32, + inodestart: u32, + bmapstart: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dinode { + typ: u16, + major: u16, + minor: u16, + nlink: u16, + size: u32, + addrs: [u32; NDIRECT + 1], +} + +impl Default for Dinode { + fn default() -> Self { + Self { + typ: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dirent { + inum: u16, + name: [u8; DIRSIZ], +} + +impl Default for Dirent { + fn default() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct Mkfs { + fsfd: File, + sb: Superblock, + freeinode: u32, + freeblock: u32, +} + +fn as_bytes(val: &T) -> &[u8] { + unsafe { std::slice::from_raw_parts((val as *const T).cast::(), size_of::()) } +} + +fn from_bytes(bytes: &[u8]) -> T { + assert_eq!(bytes.len(), size_of::()); + let mut out = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::copy_nonoverlapping(bytes.as_ptr(), out.as_mut_ptr().cast::(), bytes.len()); + out.assume_init() + } +} + +fn name_to_dirent(name: &str, inum: u16) -> Dirent { + let mut de = Dirent { + inum: inum.to_le(), + ..Default::default() + }; + let name_bytes = name.as_bytes(); + let copy_len = name_bytes.len().min(DIRSIZ); + de.name[..copy_len].copy_from_slice(&name_bytes[..copy_len]); + de +} + +impl Mkfs { + fn new(fsfd: File, sb: Superblock, freeblock: u32) -> Self { + Self { + fsfd, + sb, + freeinode: 1, + freeblock, + } + } + + fn wsect(&mut self, sec: u32, buf: &[u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(write) failed"); + self.fsfd.write_all(buf).expect("write failed"); + } + + fn rsect(&mut self, sec: u32, buf: &mut [u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(read) failed"); + self.fsfd.read_exact(buf).expect("read failed"); + } + + fn iblock(&self, inum: u32) -> u32 { + (inum / ipb() as u32) + u32::from_le(self.sb.inodestart) + } + + fn winode(&mut self, inum: u32, ip: &Dinode) { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + let src = as_bytes(ip); + buf[off..off + size_of::()].copy_from_slice(src); + self.wsect(bn, &buf); + } + + fn rinode(&mut self, inum: u32) -> Dinode { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + from_bytes::(&buf[off..off + size_of::()]) + } + + fn ialloc(&mut self, typ: u16) -> u32 { + let inum = self.freeinode; + self.freeinode += 1; + + let din = Dinode { + typ: typ.to_le(), + nlink: 1u16.to_le(), + size: 0u32.to_le(), + ..Default::default() + }; + self.winode(inum, &din); + inum + } + + fn balloc(&mut self, used: u32) { + assert!(used < (BSIZE * 8) as u32); + println!("balloc: first {} blocks have been allocated", used); + + let mut buf = [0u8; BSIZE]; + for i in 0..used { + let idx = (i / 8) as usize; + let bit = (i % 8) as u8; + buf[idx] |= 1u8 << bit; + } + + let bmapstart = u32::from_le(self.sb.bmapstart); + println!("balloc: write bitmap block at sector {}", bmapstart); + self.wsect(bmapstart, &buf); + } + + fn iappend(&mut self, inum: u32, mut p: &[u8]) { + let mut din = self.rinode(inum); + let mut off = u32::from_le(din.size); + + while !p.is_empty() { + let fbn = (off as usize) / BSIZE; + assert!(fbn < MAXFILE); + + let x: u32; + if fbn < NDIRECT { + if u32::from_le(din.addrs[fbn]) == 0 { + din.addrs[fbn] = self.freeblock.to_le(); + self.freeblock += 1; + } + x = u32::from_le(din.addrs[fbn]); + } else { + if u32::from_le(din.addrs[NDIRECT]) == 0 { + din.addrs[NDIRECT] = self.freeblock.to_le(); + self.freeblock += 1; + } + + let mut indirect_sector = [0u8; BSIZE]; + let indirect_blockno = u32::from_le(din.addrs[NDIRECT]); + self.rsect(indirect_blockno, &mut indirect_sector); + + let indirect_idx = fbn - NDIRECT; + let byte_off = indirect_idx * size_of::(); + let mut indirect_entry = u32::from_le(from_bytes::( + &indirect_sector[byte_off..byte_off + size_of::()], + )); + + if indirect_entry == 0 { + indirect_entry = self.freeblock; + self.freeblock += 1; + let le = indirect_entry.to_le(); + indirect_sector[byte_off..byte_off + size_of::()] + .copy_from_slice(as_bytes(&le)); + self.wsect(indirect_blockno, &indirect_sector); + } + + x = indirect_entry; + } + + let mut buf = [0u8; BSIZE]; + self.rsect(x, &mut buf); + + let n1 = p + .len() + .min((fbn as u32 + 1) as usize * BSIZE - off as usize); + let block_off = off as usize - (fbn * BSIZE); + buf[block_off..block_off + n1].copy_from_slice(&p[..n1]); + self.wsect(x, &buf); + + off += n1 as u32; + p = &p[n1..]; + } + + din.size = off.to_le(); + self.winode(inum, &din); + } +} + +fn ipb() -> usize { + BSIZE / size_of::() +} + +fn main() { + assert_eq!(size_of::(), 4, "Integers must be 4 bytes"); + assert_eq!(BSIZE % size_of::(), 0); + assert_eq!(BSIZE % size_of::(), 0); + + let mut args = env::args().collect::>(); + if args.len() < 2 { + eprintln!("Usage: mkfs fs.img files..."); + std::process::exit(1); + } + + let fs_img = args.remove(1); + + let nbitmap = FSSIZE / (BSIZE as u32 * 8) + 1; + let ninodeblocks = NINODES / ipb() as u32 + 1; + let nlog = LOGSIZE; + let nmeta = 2 + nlog + ninodeblocks + nbitmap; + let nblocks = FSSIZE - nmeta; + + let sb = Superblock { + size: FSSIZE.to_le(), + nblocks: nblocks.to_le(), + ninodes: NINODES.to_le(), + nlog: nlog.to_le(), + logstart: 2u32.to_le(), + inodestart: (2 + nlog).to_le(), + bmapstart: (2 + nlog + ninodeblocks).to_le(), + }; + + println!( + "nmeta {} (boot, super, log blocks {} inode blocks {}, bitmap blocks {}) blocks {} total {}", + nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE + ); + + let fsfd = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fs_img) + .unwrap_or_else(|e| { + eprintln!("{}: {}", fs_img, e); + std::process::exit(1); + }); + + let mut mkfs = Mkfs::new(fsfd, sb, nmeta); + + let zeroes = [0u8; BSIZE]; + for i in 0..FSSIZE { + mkfs.wsect(i, &zeroes); + } + + let mut sb_buf = [0u8; BSIZE]; + sb_buf[..size_of::()].copy_from_slice(as_bytes(&mkfs.sb)); + mkfs.wsect(1, &sb_buf); + + let rootino = mkfs.ialloc(T_DIR); + assert_eq!(rootino, ROOTINO); + + let dot = name_to_dirent(".", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dot)); + + let dotdot = name_to_dirent("..", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dotdot)); + + for path in &args[1..] { + if path.contains('/') { + eprintln!("{}: must be a basename (no /)", path); + std::process::exit(1); + } + + let mut host_file = File::open(path).unwrap_or_else(|e| { + eprintln!("{}: {}", path, e); + std::process::exit(1); + }); + + let file_name = if let Some(stripped) = path.strip_prefix('_') { + stripped + } else { + Path::new(path) + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(path) + }; + + let inum = mkfs.ialloc(T_FILE); + let de = name_to_dirent(file_name, inum as u16); + mkfs.iappend(rootino, as_bytes(&de)); + + let mut buf = [0u8; BSIZE]; + loop { + let cc = host_file.read(&mut buf).expect("read input file failed"); + if cc == 0 { + break; + } + mkfs.iappend(inum, &buf[..cc]); + } + } + + // fix size of root inode dir + let mut din = mkfs.rinode(rootino); + let mut off = u32::from_le(din.size); + off = ((off / BSIZE as u32) + 1) * BSIZE as u32; + din.size = off.to_le(); + mkfs.winode(rootino, &din); + + mkfs.balloc(mkfs.freeblock); +} \ No newline at end of file From 626cd94535d2c07471291737021bb29ade1b1ce3 Mon Sep 17 00:00:00 2001 From: legends1307 Date: Mon, 16 Feb 2026 22:32:14 +0530 Subject: [PATCH 12/69] p9: add name layer + self-contained Rust mkfs - Added src/mkfs.rs: pure Rust mkfs tool (based on sudoheckbeluga's work in PR #5), adapted with FSSIZE=1000 and LOGSIZE=0 to match the C repo's p9-name-layer branch. No more ../col331/ dependency. - Updated Makefile: mkfs now compiled with rustc from src/mkfs.rs - Added name layer functions in src/fs.rs: irelse, namecmp, dirlookup, skipelem, namex, namei, nameiparent - Updated welcome() in src/lib.rs to use namei("/welcome.txt") --- src/fs.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 62fb447..73944d9 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -335,7 +335,6 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { // ===================================================================== /// Compare two directory entry names (up to DIRSIZ bytes). -/// Mirrors C: int namecmp(const char *s, const char *t) { return strncmp(s, t, DIRSIZ); } pub fn namecmp(s: &[u8], t: &[u8]) -> bool { let slen = s.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(s.len())); let tlen = t.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(t.len())); @@ -352,7 +351,6 @@ pub fn namecmp(s: &[u8], t: &[u8]) -> bool { /// Look for a directory entry in a directory inode. /// If found, return the inode cache index of the entry. -/// Mirrors C: struct inode* dirlookup(struct inode *dp, char *name, uint *poff) pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { unsafe { if dp_idx >= NINODE { @@ -398,12 +396,6 @@ pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { /// Copy the next path element from path into name. /// Return the remaining path after the element (with leading slashes stripped), /// or None if there is no element to extract. -/// -/// Mirrors C: -/// skipelem("a/bb/c", name) = "bb/c", setting name = "a" -/// skipelem("///a//bb", name) = "bb", setting name = "a" -/// skipelem("a", name) = "", setting name = "a" -/// skipelem("", name) = skipelem("////", name) = 0 fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { let mut i = 0; @@ -445,8 +437,6 @@ fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { /// Look up and return the inode cache index for a path name. /// If nameiparent_flag is true, return the inode for the parent and copy /// the final path element into name. -/// -/// Mirrors C: static struct inode* namex(char *path, int nameiparent, char *name) fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option { let mut ip = iget(crate::param::ROOTDEV, ROOTINO); From f4595fce2761859d364ec659d0b30601f68f3382 Mon Sep 17 00:00:00 2001 From: Anoop Singh Date: Tue, 17 Feb 2026 18:38:34 +0530 Subject: [PATCH 13/69] p11-rust-done --- Makefile | 7 +- src/Makefile | 6 +- src/fcntl.rs | 4 + src/file.rs | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/fs.rs | 164 ++++++++++++++++++++--- src/lib.rs | 91 ++++++------- src/mkfs.rs | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/param.rs | 2 + 8 files changed, 923 insertions(+), 73 deletions(-) create mode 100644 src/fcntl.rs create mode 100644 src/file.rs create mode 100644 src/mkfs.rs diff --git a/Makefile b/Makefile index 0df672e..9397bcd 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ echo "***" 1>&2; exit 1; fi) endif +TOOLPREFIX=i686-elf- # If the makefile can't find QEMU, specify its path here # QEMU = qemu-system-i386 @@ -69,8 +70,8 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col331/param.h - gcc -Werror -Wall -o mkfs ../col331/mkfs.c +mkfs: src/mkfs.rs + rustc -W warnings -o mkfs src/mkfs.rs fs.img: mkfs welcome.txt ./mkfs fs.img welcome.txt @@ -87,7 +88,7 @@ kernel.a: $(RS) 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 diff --git a/src/Makefile b/src/Makefile index 079b0db..6061c30 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,8 +69,8 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -mkfs: ../../col331/mkfs.c ../../col331/fs.h ../../col331/types.h ../../col331/stat.h ../../col331/param.h - gcc -Werror -Wall -o mkfs ../../col331/mkfs.c +mkfs: mkfs.rs + rustc -W warnings -o mkfs mkfs.rs fs: fs.img @@ -90,7 +90,7 @@ kernel.a: $(RS) 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 diff --git a/src/fcntl.rs b/src/fcntl.rs new file mode 100644 index 0000000..2e76613 --- /dev/null +++ b/src/fcntl.rs @@ -0,0 +1,4 @@ +pub const O_RDONLY: i32 = 0x000; +pub const O_WRONLY: i32 = 0x001; +pub const O_RDWR: i32 = 0x002; +pub const O_CREATE: i32 = 0x200; diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..20e4ac5 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,364 @@ +use core::str; + +use crate::buf::BSIZE; +use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; +use crate::fs; +use crate::param::{MAXOPBLOCKS, NFILE}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum FileType { + None, + Inode, +} + +#[derive(Copy, Clone)] +pub struct File { + pub type_: FileType, + pub refcnt: i32, + pub readable: bool, + pub writable: bool, + pub ip: usize, + pub off: u32, +} + +impl File { + pub const fn new() -> Self { + Self { + type_: FileType::None, + refcnt: 0, + readable: false, + writable: false, + ip: 0, + off: 0, + } + } +} + +struct FTable { + file: [File; NFILE], +} + +impl FTable { + const fn new() -> Self { + Self { + file: [const { File::new() }; NFILE], + } + } +} + +static mut FTABLE: FTable = FTable::new(); + +fn dirsiz_to_str(name: &[u8; fs::DIRSIZ]) -> &str { + let len = name.iter().position(|&b| b == 0).unwrap_or(fs::DIRSIZ); + str::from_utf8(&name[..len]).unwrap_or("") +} + +pub fn fileinit() { + unsafe { + for i in 0..NFILE { + FTABLE.file[i] = File::new(); + } + } +} + +pub fn filealloc() -> Option { + unsafe { + for i in 0..NFILE { + if FTABLE.file[i].refcnt == 0 { + FTABLE.file[i].refcnt = 1; + return Some(i); + } + } + } + None +} + +pub fn filedup(f_idx: usize) -> usize { + unsafe { + if f_idx >= NFILE { + panic!("filedup: bad file index"); + } + if FTABLE.file[f_idx].refcnt < 1 { + panic!("filedup"); + } + FTABLE.file[f_idx].refcnt += 1; + } + f_idx +} + +pub fn fileclose(f_idx: usize) { + let ff: File; + unsafe { + if f_idx >= NFILE { + panic!("fileclose: bad file index"); + } + + let f = &mut FTABLE.file[f_idx]; + if f.refcnt < 1 { + panic!("fileclose"); + } + f.refcnt -= 1; + if f.refcnt > 0 { + return; + } + + ff = *f; + *f = File::new(); + } + + if ff.type_ == FileType::Inode { + fs::iput(ff.ip); + } +} + +pub fn filestat(f_idx: usize, st: &mut fs::Stat) -> i32 { + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("filestat: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if f.type_ == FileType::Inode { + fs::iread(f.ip); + fs::stati(f.ip, st); + return 0; + } + + -1 +} + +pub fn fileread(f_idx: usize, dst: &mut [u8], n: i32) -> i32 { + if n < 0 || (n as usize) > dst.len() { + return -1; + } + + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("fileread: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if !f.readable { + return -1; + } + + if f.type_ == FileType::Inode { + fs::iread(f.ip); + let r = fs::readi(f.ip, dst, f.off, n as u32); + if r > 0 { + unsafe { + FTABLE.file[f_idx].off += r as u32; + } + } + return r; + } + + panic!("fileread"); +} + +pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { + if n < 0 || (n as usize) > src.len() { + return -1; + } + + let f: File; + unsafe { + if f_idx >= NFILE { + panic!("filewrite: bad file index"); + } + f = FTABLE.file[f_idx]; + } + + if !f.writable { + return -1; + } + + if f.type_ == FileType::Inode { + let max = (((MAXOPBLOCKS - 1 - 1 - 2) / 2) * BSIZE) as i32; + let mut i = 0i32; + + while i < n { + let mut n1 = n - i; + if n1 > max { + n1 = max; + } + + fs::iread(f.ip); + let off = unsafe { FTABLE.file[f_idx].off }; + let r = fs::writei(f.ip, &src[i as usize..], off, n1 as u32); + if r > 0 { + unsafe { + FTABLE.file[f_idx].off += r as u32; + } + } + + if r < 0 { + break; + } + if r != n1 { + panic!("short filewrite"); + } + i += r; + } + + if i == n { + n + } else { + -1 + } + } else { + panic!("filewrite"); + } +} + +pub fn isdirempty(dp_idx: usize) -> bool { + fs::iread(dp_idx); + + let mut off = (2 * fs::DIRENT_SIZE) as u32; + while off < fs::inode_size(dp_idx) { + let mut raw = [0u8; fs::DIRENT_SIZE]; + if fs::readi(dp_idx, &mut raw, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + panic!("isdirempty: readi"); + } + let de = fs::parse_dirent(&raw); + if de.inum != 0 { + return false; + } + off += fs::DIRENT_SIZE as u32; + } + + true +} + +pub fn unlink(path: &str, name: &mut [u8; fs::DIRSIZ]) -> i32 { + let dp = match fs::nameiparent(path, name) { + Some(idx) => idx, + None => return -1, + }; + + fs::iread(dp); + + let name_str = dirsiz_to_str(name); + + if name_str == "." || name_str == ".." { + fs::iput(dp); + return -1; + } + + let mut off = 0u32; + let ip = match fs::dirlookup(dp, name_str, Some(&mut off)) { + Some(idx) => idx, + None => { + fs::iput(dp); + return -1; + } + }; + + fs::iread(ip); + + if fs::inode_nlink(ip) < 1 { + panic!("unlink: nlink < 1"); + } + + if fs::inode_type(ip) == fs::T_DIR && !isdirempty(ip) { + fs::iput(ip); + fs::iput(dp); + return -1; + } + + let de = [0u8; fs::DIRENT_SIZE]; + if fs::writei(dp, &de, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + panic!("unlink: writei"); + } + + if fs::inode_type(ip) == fs::T_DIR { + fs::inode_dec_nlink(dp); + fs::iupdate(dp); + } + fs::iput(dp); + + fs::inode_dec_nlink(ip); + fs::iupdate(ip); + fs::iput(ip); + + 0 +} + +pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { + let mut name = [0u8; fs::DIRSIZ]; + + let dp = fs::nameiparent(path, &mut name)?; + fs::iread(dp); + + let name_str = dirsiz_to_str(&name); + + if let Some(ip) = fs::dirlookup(dp, name_str, None) { + fs::iput(dp); + fs::iread(ip); + if type_ == fs::T_FILE && fs::inode_type(ip) == fs::T_FILE { + return Some(ip); + } + fs::iput(ip); + return None; + } + + let ip = fs::ialloc(fs::inode_dev(dp), type_); + + fs::iread(ip); + fs::inode_set_meta(ip, major, minor, 1); + fs::iupdate(ip); + + if type_ == fs::T_DIR { + fs::inode_inc_nlink(dp); + fs::iupdate(dp); + + if fs::dirlink(ip, ".", fs::inode_inum(ip)) < 0 || fs::dirlink(ip, "..", fs::inode_inum(dp)) < 0 { + panic!("create dots"); + } + } + + if fs::dirlink(dp, name_str, fs::inode_inum(ip)) < 0 { + panic!("create: dirlink"); + } + + fs::iput(dp); + + Some(ip) +} + +pub fn open(path: &str, omode: i32) -> Option { + let ip = if (omode & O_CREATE) != 0 { + create(path, fs::T_FILE, 0, 0)? + } else { + let ip = fs::namei(path)?; + fs::iread(ip); + if fs::inode_type(ip) == fs::T_DIR && omode != O_RDONLY { + fs::iput(ip); + return None; + } + ip + }; + + let f_idx = match filealloc() { + Some(idx) => idx, + None => { + fs::iput(ip); + return None; + } + }; + + unsafe { + let f = &mut FTABLE.file[f_idx]; + f.type_ = FileType::Inode; + f.ip = ip; + f.off = 0; + f.readable = (omode & O_WRONLY) == 0; + f.writable = (omode & O_WRONLY) != 0 || (omode & O_RDWR) != 0; + } + + Some(f_idx) +} diff --git a/src/fs.rs b/src/fs.rs index e934c01..3c4079b 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,7 +2,7 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; -use crate::param::NINODE; +use crate::param::{NINODE, ROOTDEV}; use crate::println; pub const ROOTINO: u32 = 1; @@ -254,6 +254,26 @@ fn balloc(dev: u32) -> u32 { panic!("balloc: out of blocks"); } +fn bfree(dev: u32, b: u32) { + unsafe { + let bp = bio::bread(dev, bblock(b, &SB)); + let bi = b % BPB; + let m: u8 = 1u8 << (bi % 8); + let idx = (bi / 8) as usize; + + { + let data = &mut bio::buf_mut(bp).data; + if (data[idx] & m) == 0 { + panic!("freeing free block"); + } + data[idx] &= !m; + } + + bio::bwrite(bp); + bio::brelse(bp); + } +} + pub fn ialloc(dev: u32, type_: i16) -> usize { unsafe { let mut inum = 1u32; @@ -285,18 +305,69 @@ pub fn ialloc(dev: u32, type_: i16) -> usize { panic!("ialloc: no inodes"); } -pub fn irelease(idx: usize) { +fn itrunc(idx: usize) { unsafe { if idx >= NINODE { - panic!("irelease: bad inode index"); + panic!("itrunc: bad inode index"); + } + + let dev = ICACHE.inode[idx].dev; + for i in 0..NDIRECT { + let addr = ICACHE.inode[idx].addrs[i]; + if addr != 0 { + bfree(dev, addr); + ICACHE.inode[idx].addrs[i] = 0; + } + } + + let indirect = ICACHE.inode[idx].addrs[NDIRECT]; + if indirect != 0 { + let bp = bio::bread(dev, indirect); + for j in 0..NINDIRECT { + let off = j * 4; + let a = { + let data = &bio::buf_mut(bp).data; + read_u32_le(data, off) + }; + if a != 0 { + bfree(dev, a); + } + } + bio::brelse(bp); + bfree(dev, indirect); + ICACHE.inode[idx].addrs[NDIRECT] = 0; + } + + ICACHE.inode[idx].size = 0; + } + + iupdate(idx); +} + +pub fn iput(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("iput: bad inode index"); } if ICACHE.inode[idx].refcnt < 1 { - panic!("irelease: ref underflow"); + panic!("iput: ref underflow"); + } + + if ICACHE.inode[idx].valid != 0 && ICACHE.inode[idx].nlink == 0 && ICACHE.inode[idx].refcnt == 1 { + itrunc(idx); + ICACHE.inode[idx].type_ = 0; + iupdate(idx); + ICACHE.inode[idx].valid = 0; } + ICACHE.inode[idx].refcnt -= 1; } } +pub fn irelease(idx: usize) { + iput(idx); +} + pub fn iupdate(idx: usize) { unsafe { if idx >= NINODE { @@ -454,7 +525,7 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; - if off > ip.size || off.checked_add(n).is_none() { + if off > ip.size || off.checked_add(n).is_none() || ip.nlink < 1 { return -1; } @@ -540,15 +611,78 @@ pub fn inode_inum(idx: usize) -> u32 { } } +pub fn inode_dev(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_dev: bad inode index"); + } + ICACHE.inode[idx].dev + } +} + +pub fn inode_type(idx: usize) -> i16 { + unsafe { + if idx >= NINODE { + panic!("inode_type: bad inode index"); + } + ICACHE.inode[idx].type_ + } +} + +pub fn inode_nlink(idx: usize) -> i16 { + unsafe { + if idx >= NINODE { + panic!("inode_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink + } +} + +pub fn inode_size(idx: usize) -> u32 { + unsafe { + if idx >= NINODE { + panic!("inode_size: bad inode index"); + } + ICACHE.inode[idx].size + } +} + +pub fn inode_set_meta(idx: usize, major: i16, minor: i16, nlink: i16) { + unsafe { + if idx >= NINODE { + panic!("inode_set_meta: bad inode index"); + } + ICACHE.inode[idx].major = major; + ICACHE.inode[idx].minor = minor; + ICACHE.inode[idx].nlink = nlink; + } +} + +pub fn inode_inc_nlink(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("inode_inc_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink += 1; + } +} + +pub fn inode_dec_nlink(idx: usize) { + unsafe { + if idx >= NINODE { + panic!("inode_dec_nlink: bad inode index"); + } + ICACHE.inode[idx].nlink -= 1; + } +} + pub fn namecmp(s: &str, t: &[u8; DIRSIZ]) -> bool { name_to_dirsiz(s) == *t } pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Option { - unsafe { - if dp_idx >= NINODE { - panic!("dirlookup: bad inode index"); - } + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); } iread(dp_idx); @@ -580,7 +714,7 @@ pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Optio pub fn dirlink(dp_idx: usize, name: &str, inum: u32) -> i32 { if let Some(ip_idx) = dirlookup(dp_idx, name, None) { - irelease(ip_idx); + iput(ip_idx); return -1; } @@ -642,7 +776,7 @@ fn skipelem(path: &[u8], mut i: usize, name: &mut [u8; DIRSIZ]) -> Option fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option { let bytes = path.as_bytes(); let mut path_idx = 0usize; - let mut ip = iget(crate::param::ROOTDEV, ROOTINO); + let mut ip = iget(ROOTDEV, ROOTINO); while let Some(next_idx) = skipelem(bytes, path_idx, name) { path_idx = next_idx; @@ -650,7 +784,7 @@ fn namex(path: &str, nameiparent: bool, name: &mut [u8; DIRSIZ]) -> Option Option x, None => { - irelease(ip); + iput(ip); return None; } }; - irelease(ip); + iput(ip); ip = next; } if nameiparent { - irelease(ip); + iput(ip); return None; } Some(ip) diff --git a/src/lib.rs b/src/lib.rs index 42b72f5..db5d4d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,8 @@ mod buf; mod bio; mod ide; mod fs; +mod file; +mod fcntl; use crate::traps::*; #[macro_export] @@ -38,12 +40,6 @@ fn halt() -> ! { } } -fn print_bytes(bytes: &[u8]) { - for &ch in bytes { - console::consputc(ch as char); - } -} - fn print_cstr(bytes: &[u8]) { for &ch in bytes { if ch == 0 { @@ -54,56 +50,46 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let root = fs::namei("/").unwrap_or_else(|| panic!("root not found")); - fs::iread(root); - let foodir = match fs::dirlookup(root, "foo", None) { - Some(idx) => idx, - None => { - println!("/foo not found. Creating!"); - let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); - fs::iread(idx); - let ino = fs::inode_inum(idx); - if fs::dirlink(idx, ".", ino) < 0 { - panic!("failed to link . in /foo"); - } - if fs::dirlink(idx, "..", ino) < 0 { - panic!("failed to link .. in /foo"); - } - if fs::dirlink(root, "foo", ino) < 0 { - panic!("failed to link /foo in root"); - } - idx - } - }; - - let wtxt = match fs::namei("/foo/greet.txt") { - Some(idx) => idx, - None => { - println!("/foo/greet.txt not found. Creating!"); - let wtxt_orig = - fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); - let inum = fs::inode_inum(wtxt_orig); - if fs::dirlink(foodir, "greet.txt", inum) < 0 { - panic!("failed to link greet.txt in /foo"); - } - fs::irelease(wtxt_orig); - fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) - } - }; + let _ = file::create("/foo", fs::T_DIR, 0, 0); - fs::iread(wtxt); - let mut st = fs::Stat::new(); - fs::stati(wtxt, &mut st); + let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) + .unwrap_or_else(|| panic!("failed to create /foo/hello.txt")); + let n = file::filewrite(gtxt, b"hello\0", 6); + println!("Wrote {} characters to /foo/hello.txt", n); + file::fileclose(gtxt); - let mut greet = [0u8; 512]; - let n = fs::readi(wtxt, &mut greet, 0, st.size); - println!("Read {} bytes from /foo/greet.txt", n); - print_bytes(&greet[..n as usize]); + let gtxt = file::open("/foo/hello.txt", fcntl::O_RDONLY) + .unwrap_or_else(|| panic!("unable to open /foo/hello.txt")); + let mut welcome = [0u8; 512]; + let n = file::fileread(gtxt, &mut welcome, 6); + println!("Read {} chars from /foo/hello.txt: ", n); + print_cstr(&welcome); console::consputc('\n'); + file::fileclose(gtxt); + + let mut name = [0u8; fs::DIRSIZ]; + if file::unlink("/foo/hello.txt", &mut name) < 0 { + panic!("failed to unlink /foo/hello.txt"); + } + + let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); + if !file::isdirempty(foo) { + panic!("/foo should be empty"); + } + fs::iput(foo); + + if let Some(f) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { + file::fileclose(f); + panic!("could open /foo/hello.txt after unlinking"); + } - fs::irelease(wtxt); - fs::irelease(foodir); - fs::irelease(root); + let wtxt = + file::open("/welcome.txt", fcntl::O_RDONLY).unwrap_or_else(|| panic!("unable to open /welcome.txt")); + let welcome_cap = welcome.len() as i32; + let n = file::fileread(wtxt, &mut welcome, welcome_cap); + println!("Read {} chars from /welcome.txt:", n); + print_cstr(&welcome); + file::fileclose(wtxt); } extern "C" { @@ -123,6 +109,7 @@ pub extern "C" fn entryofrust() -> ! { idtinit(); x86::sti(); fs::iinit(param::ROOTDEV); + file::fileinit(); welcome(); loop { diff --git a/src/mkfs.rs b/src/mkfs.rs new file mode 100644 index 0000000..6363752 --- /dev/null +++ b/src/mkfs.rs @@ -0,0 +1,358 @@ +use std::env; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::mem::size_of; +use std::path::Path; + +// File system constants — must match param.h / fs.h from C repo p9-name-layer +const BSIZE: usize = 512; +const FSSIZE: u32 = 1000; +const NINODES: u32 = 200; +const LOGSIZE: u32 = 0; + +const ROOTINO: u32 = 1; +const NDIRECT: usize = 12; +const NINDIRECT: usize = BSIZE / size_of::(); +const MAXFILE: usize = NDIRECT + NINDIRECT; +const DIRSIZ: usize = 14; + +const T_DIR: u16 = 1; +const T_FILE: u16 = 2; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct Superblock { + size: u32, + nblocks: u32, + ninodes: u32, + nlog: u32, + logstart: u32, + inodestart: u32, + bmapstart: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dinode { + typ: u16, + major: u16, + minor: u16, + nlink: u16, + size: u32, + addrs: [u32; NDIRECT + 1], +} + +impl Default for Dinode { + fn default() -> Self { + Self { + typ: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Dirent { + inum: u16, + name: [u8; DIRSIZ], +} + +impl Default for Dirent { + fn default() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} + +struct Mkfs { + fsfd: File, + sb: Superblock, + freeinode: u32, + freeblock: u32, +} + +fn as_bytes(val: &T) -> &[u8] { + unsafe { std::slice::from_raw_parts((val as *const T).cast::(), size_of::()) } +} + +fn from_bytes(bytes: &[u8]) -> T { + assert_eq!(bytes.len(), size_of::()); + let mut out = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::copy_nonoverlapping(bytes.as_ptr(), out.as_mut_ptr().cast::(), bytes.len()); + out.assume_init() + } +} + +fn name_to_dirent(name: &str, inum: u16) -> Dirent { + let mut de = Dirent { + inum: inum.to_le(), + ..Default::default() + }; + let name_bytes = name.as_bytes(); + let copy_len = name_bytes.len().min(DIRSIZ); + de.name[..copy_len].copy_from_slice(&name_bytes[..copy_len]); + de +} + +impl Mkfs { + fn new(fsfd: File, sb: Superblock, freeblock: u32) -> Self { + Self { + fsfd, + sb, + freeinode: 1, + freeblock, + } + } + + fn wsect(&mut self, sec: u32, buf: &[u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(write) failed"); + self.fsfd.write_all(buf).expect("write failed"); + } + + fn rsect(&mut self, sec: u32, buf: &mut [u8; BSIZE]) { + let off = (sec as u64) * (BSIZE as u64); + self.fsfd + .seek(SeekFrom::Start(off)) + .expect("lseek(read) failed"); + self.fsfd.read_exact(buf).expect("read failed"); + } + + fn iblock(&self, inum: u32) -> u32 { + (inum / ipb() as u32) + u32::from_le(self.sb.inodestart) + } + + fn winode(&mut self, inum: u32, ip: &Dinode) { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + let src = as_bytes(ip); + buf[off..off + size_of::()].copy_from_slice(src); + self.wsect(bn, &buf); + } + + fn rinode(&mut self, inum: u32) -> Dinode { + let mut buf = [0u8; BSIZE]; + let bn = self.iblock(inum); + self.rsect(bn, &mut buf); + + let off = (inum as usize % ipb()) * size_of::(); + from_bytes::(&buf[off..off + size_of::()]) + } + + fn ialloc(&mut self, typ: u16) -> u32 { + let inum = self.freeinode; + self.freeinode += 1; + + let din = Dinode { + typ: typ.to_le(), + nlink: 1u16.to_le(), + size: 0u32.to_le(), + ..Default::default() + }; + self.winode(inum, &din); + inum + } + + fn balloc(&mut self, used: u32) { + assert!(used < (BSIZE * 8) as u32); + println!("balloc: first {} blocks have been allocated", used); + + let mut buf = [0u8; BSIZE]; + for i in 0..used { + let idx = (i / 8) as usize; + let bit = (i % 8) as u8; + buf[idx] |= 1u8 << bit; + } + + let bmapstart = u32::from_le(self.sb.bmapstart); + println!("balloc: write bitmap block at sector {}", bmapstart); + self.wsect(bmapstart, &buf); + } + + fn iappend(&mut self, inum: u32, mut p: &[u8]) { + let mut din = self.rinode(inum); + let mut off = u32::from_le(din.size); + + while !p.is_empty() { + let fbn = (off as usize) / BSIZE; + assert!(fbn < MAXFILE); + + let x: u32; + if fbn < NDIRECT { + if u32::from_le(din.addrs[fbn]) == 0 { + din.addrs[fbn] = self.freeblock.to_le(); + self.freeblock += 1; + } + x = u32::from_le(din.addrs[fbn]); + } else { + if u32::from_le(din.addrs[NDIRECT]) == 0 { + din.addrs[NDIRECT] = self.freeblock.to_le(); + self.freeblock += 1; + } + + let mut indirect_sector = [0u8; BSIZE]; + let indirect_blockno = u32::from_le(din.addrs[NDIRECT]); + self.rsect(indirect_blockno, &mut indirect_sector); + + let indirect_idx = fbn - NDIRECT; + let byte_off = indirect_idx * size_of::(); + let mut indirect_entry = u32::from_le(from_bytes::( + &indirect_sector[byte_off..byte_off + size_of::()], + )); + + if indirect_entry == 0 { + indirect_entry = self.freeblock; + self.freeblock += 1; + let le = indirect_entry.to_le(); + indirect_sector[byte_off..byte_off + size_of::()] + .copy_from_slice(as_bytes(&le)); + self.wsect(indirect_blockno, &indirect_sector); + } + + x = indirect_entry; + } + + let mut buf = [0u8; BSIZE]; + self.rsect(x, &mut buf); + + let n1 = p + .len() + .min((fbn as u32 + 1) as usize * BSIZE - off as usize); + let block_off = off as usize - (fbn * BSIZE); + buf[block_off..block_off + n1].copy_from_slice(&p[..n1]); + self.wsect(x, &buf); + + off += n1 as u32; + p = &p[n1..]; + } + + din.size = off.to_le(); + self.winode(inum, &din); + } +} + +fn ipb() -> usize { + BSIZE / size_of::() +} + +fn main() { + assert_eq!(size_of::(), 4, "Integers must be 4 bytes"); + assert_eq!(BSIZE % size_of::(), 0); + assert_eq!(BSIZE % size_of::(), 0); + + let mut args = env::args().collect::>(); + if args.len() < 2 { + eprintln!("Usage: mkfs fs.img files..."); + std::process::exit(1); + } + + let fs_img = args.remove(1); + + let nbitmap = FSSIZE / (BSIZE as u32 * 8) + 1; + let ninodeblocks = NINODES / ipb() as u32 + 1; + let nlog = LOGSIZE; + let nmeta = 2 + nlog + ninodeblocks + nbitmap; + let nblocks = FSSIZE - nmeta; + + let sb = Superblock { + size: FSSIZE.to_le(), + nblocks: nblocks.to_le(), + ninodes: NINODES.to_le(), + nlog: nlog.to_le(), + logstart: 2u32.to_le(), + inodestart: (2 + nlog).to_le(), + bmapstart: (2 + nlog + ninodeblocks).to_le(), + }; + + println!( + "nmeta {} (boot, super, log blocks {} inode blocks {}, bitmap blocks {}) blocks {} total {}", + nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE + ); + + let fsfd = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&fs_img) + .unwrap_or_else(|e| { + eprintln!("{}: {}", fs_img, e); + std::process::exit(1); + }); + + let mut mkfs = Mkfs::new(fsfd, sb, nmeta); + + let zeroes = [0u8; BSIZE]; + for i in 0..FSSIZE { + mkfs.wsect(i, &zeroes); + } + + let mut sb_buf = [0u8; BSIZE]; + sb_buf[..size_of::()].copy_from_slice(as_bytes(&mkfs.sb)); + mkfs.wsect(1, &sb_buf); + + let rootino = mkfs.ialloc(T_DIR); + assert_eq!(rootino, ROOTINO); + + let dot = name_to_dirent(".", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dot)); + + let dotdot = name_to_dirent("..", rootino as u16); + mkfs.iappend(rootino, as_bytes(&dotdot)); + + for path in &args[1..] { + if path.contains('/') { + eprintln!("{}: must be a basename (no /)", path); + std::process::exit(1); + } + + let mut host_file = File::open(path).unwrap_or_else(|e| { + eprintln!("{}: {}", path, e); + std::process::exit(1); + }); + + let file_name = if let Some(stripped) = path.strip_prefix('_') { + stripped + } else { + Path::new(path) + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(path) + }; + + let inum = mkfs.ialloc(T_FILE); + let de = name_to_dirent(file_name, inum as u16); + mkfs.iappend(rootino, as_bytes(&de)); + + let mut buf = [0u8; BSIZE]; + loop { + let cc = host_file.read(&mut buf).expect("read input file failed"); + if cc == 0 { + break; + } + mkfs.iappend(inum, &buf[..cc]); + } + } + + // fix size of root inode dir + let mut din = mkfs.rinode(rootino); + let mut off = u32::from_le(din.size); + off = ((off / BSIZE as u32) + 1) * BSIZE as u32; + din.size = off.to_le(); + mkfs.winode(rootino, &din); + + mkfs.balloc(mkfs.freeblock); +} \ No newline at end of file diff --git a/src/param.rs b/src/param.rs index 63a19c7..7c8176a 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,4 +1,6 @@ pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack pub const NCPU: usize = 8; // maximum number of CPUs +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk From 08c2e4e733ddc4c4c91b25c5629e16e50cf72604 Mon Sep 17 00:00:00 2001 From: Anoop Singh Date: Tue, 17 Feb 2026 18:47:11 +0530 Subject: [PATCH 14/69] cleaned --- Makefile | 7 +-- README | 5 -- README.md | 50 ++++++++++++++++++++ src/Makefile | 130 --------------------------------------------------- 4 files changed, 54 insertions(+), 138 deletions(-) delete mode 100644 README create mode 100644 README.md delete mode 100644 src/Makefile diff --git a/Makefile b/Makefile index 9397bcd..f383e75 100644 --- a/Makefile +++ b/Makefile @@ -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; \ @@ -23,7 +25,6 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ echo "***" 1>&2; exit 1; fi) endif -TOOLPREFIX=i686-elf- # If the makefile can't find QEMU, specify its path here # QEMU = qemu-system-i386 diff --git a/README b/README deleted file mode 100644 index 14ca193..0000000 --- a/README +++ /dev/null @@ -1,5 +0,0 @@ -For the COL331 course - - -1. The next step to make things look good is to remove perl script , and that may be done using a separate linker script. -2. Or may be use inclbin inside assembly. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1305e65 --- /dev/null +++ b/README.md @@ -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 + diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 6061c30..0000000 --- a/src/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -OBJS = entry.o vectors.o trapasm.o -RS = src/*.rs - -# Cross-compiling (e.g., on Mac OS X) -#TOOLPREFIX = i386-jos-elf -#TOOLPREFIX = i386-elf- - -# Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = - -# 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; \ - then echo 'i386-jos-elf-'; \ - elif 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; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) -endif - -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU -ifndef QEMU -QEMU = $(shell if which qemu > /dev/null; \ - then echo qemu; exit; \ - elif which qemu-system-i386 > /dev/null; \ - then echo qemu-system-i386; exit; \ - elif which qemu-system-x86_64 > /dev/null; \ - then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) -endif - -CC = $(TOOLPREFIX)gcc -AS = $(TOOLPREFIX)gas -LD = $(TOOLPREFIX)ld -OBJCOPY = $(TOOLPREFIX)objcopy -OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) -ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) - -# Disable PIE when possible (for Ubuntu 16.10 toolchain) -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) -CFLAGS += -fno-pie -no-pie -endif -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) -CFLAGS += -fno-pie -nopie -endif - -# Disk image with bootblock + kernel -xv6.img: bootblock kernel - dd if=/dev/zero of=xv6.img count=10000 - dd if=bootblock of=xv6.img conv=notrunc - dd if=kernel of=xv6.img seek=1 conv=notrunc - -mkfs: mkfs.rs - rustc -W warnings -o mkfs mkfs.rs - -fs: fs.img - -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 - perl sign.pl bootblock - -kernel.a: $(RS) - 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 - $(OBJDUMP) -S -D kernel > kernel.asm - $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym - -vectors.S: vectors.pl - ./vectors.pl > vectors.S - -.PRECIOUS: %.o --include *.d - -clean: - rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ - *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs - rm -rf target - -# run in emulators -GDBPORT = $(shell expr `id -u` % 5000 + 25000) -QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ - then echo "-gdb tcp::$(GDBPORT)"; \ - else echo "-s -p $(GDBPORT)"; fi) -ifndef CPUS - CPUS := 1 -endif - -# Attach fs.img as disk1 (index=1), like the C version -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 fs.img - $(QEMU) -nographic $(QEMUOPTS) - -.gdbinit: .gdbinit.tmpl - sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ - -qemu-gdb: xv6.img fs .gdbinit - @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) From 706653547a7d55c04fe33e70f799a64557ca452c Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sun, 1 Mar 2026 03:44:16 +0000 Subject: [PATCH 15/69] Cherrypicked Improvements from p3-pic --- Cargo.toml | 1 + bootasm.S | 5 +++- entry.S | 3 ++ src/ioapic.rs | 6 ++-- src/lapic.rs | 12 ++++---- src/mp.rs | 15 ++++++---- src/param.rs | 2 +- src/x86.rs | 78 +++++++++++++++++++++++++++++---------------------- 8 files changed, 72 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2dd5beb..6c2171c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "xv6" version = "0.1.0" edition = "2021" +autobins = false [lib] name = "kernel" diff --git a/bootasm.S b/bootasm.S index 81c0d52..3121176 100644 --- a/bootasm.S +++ b/bootasm.S @@ -83,4 +83,7 @@ gdt: gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 - .long gdt # address gdt \ No newline at end of file + .long gdt # address gdt + +# Add .note.GNU-stack section for non-executable stack marking +.section .note.GNU-stack,"",@progbits \ No newline at end of file diff --git a/entry.S b/entry.S index a7f6755..26000a2 100644 --- a/entry.S +++ b/entry.S @@ -48,3 +48,6 @@ entry: call entryofrust .comm stack, KSTACKSIZE + +# Add .note.GNU-stack section for non-executable stack marking +.section .note.GNU-stack,"",@progbits diff --git a/src/ioapic.rs b/src/ioapic.rs index 15cf837..1196087 100644 --- a/src/ioapic.rs +++ b/src/ioapic.rs @@ -14,9 +14,9 @@ const REG_TABLE: u32 = 0x10; // Redirection table base (0x10 / 4) // Redirection table configuration bits const INT_DISABLED: u32 = 0x00010000; // Interrupt disabled -const INT_LEVEL: u32 = 0x00008000; // Level-triggered -const INT_ACTIVELOW: u32 = 0x00002000; // Active low -const INT_LOGICAL: u32 = 0x00000800; // Destination is CPU ID +// const INT_LEVEL: u32 = 0x00008000; // Unused in p3 - Level-triggered +// const INT_ACTIVELOW: u32 = 0x00002000; // Unused in p3 - Active low +// const INT_LOGICAL: u32 = 0x00000800; // Unused in p3 - Destination is CPU ID pub const T_IRQ0: u32 = 32; diff --git a/src/lapic.rs b/src/lapic.rs index 14f76e6..3a1aa17 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -14,14 +14,14 @@ const ESR: isize = 0x0280 / 4; const ICRLO: isize = 0x0300 / 4; const INIT: u32 = 0x00000500; -const STARTUP: u32 = 0x00000600; +// const STARTUP: u32 = 0x00000600; // Unused in p3 - for AP startup const DELIVS: u32 = 0x00001000; -const ASSERT: u32 = 0x00004000; -const DEASSERT: u32 = 0x00000000; +// const ASSERT: u32 = 0x00004000; // Unused in p3 - for IPI +// const DEASSERT: u32 = 0x00000000; // Unused in p3 - for IPI const LEVEL: u32 = 0x00008000; const BCAST: u32 = 0x00080000; -const BUSY: u32 = 0x00001000; -const FIXED: u32 = 0x00000000; +// const BUSY: u32 = 0x00001000; // Unused in p3 +// const FIXED: u32 = 0x00000000; // Unused in p3 const ICRHI: isize = 0x0310 / 4; const TIMER: isize = 0x0320 / 4; @@ -35,7 +35,7 @@ const ERROR: isize = 0x0370 / 4; const MASKED: u32 = 0x00010000; const TICR: isize = 0x0380 / 4; -const TCCR: isize = 0x0390 / 4; +// const TCCR: isize = 0x0390 / 4; // Unused in p3 - timer current count const TDCR: isize = 0x03E0 / 4; // Volatile write to LAPIC diff --git a/src/mp.rs b/src/mp.rs index 9684f51..3eecf6a 100644 --- a/src/mp.rs +++ b/src/mp.rs @@ -4,7 +4,8 @@ use crate::x86::{outb, inb}; use crate::proc::Cpu; use core::cell::OnceCell; -pub static mut IOAPICID: u8 = 0; +// Removed: replaced by MP_ONCE.ioapic_id OnceCell pattern +// pub static mut IOAPICID: u8 = 0; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -59,7 +60,7 @@ pub struct MpIoApic { } // Processor flags -pub const MPBOOT: u8 = 0x02; // This proc is the bootstrap processor +// pub const MPBOOT: u8 = 0x02; // Unused in p3 - This proc is the bootstrap processor // Table entry types pub const MPPROC: u8 = 0x00; // One per processor @@ -194,7 +195,8 @@ pub fn mpinit() { let conf = unsafe { *(mp.physaddr as *mut MpConf) }; let mut ismp = true; - MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32); + MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32) + .expect("lapic_base already initialized"); let mut p = (mp.physaddr as usize + mem::size_of::()) as *const u8; let e = (mp.physaddr as usize + conf.length as usize) as *const u8; @@ -218,8 +220,8 @@ pub fn mpinit() { MPIOAPIC => { let ioapic = p as *const MpIoApic; let ioapicid = unsafe { (*ioapic).apicno }; - MP_ONCE.ioapic_id.set(ioapicid); - // p = unsafe{ p.add(mem::size_of::()) }; + MP_ONCE.ioapic_id.set(ioapicid) + .expect("ioapic_id already initialized"); p = p.wrapping_add(mem::size_of::()); } MPBUS | MPIOINTR | MPLINTR => { @@ -236,7 +238,8 @@ pub fn mpinit() { if !ismp { panic!("Didn't find a suitable machine"); } - MP_ONCE.cpus.set(cpus); + MP_ONCE.cpus.set(cpus) + .expect("cpus array already initialized"); if mp.imcrp != 0 { // Bochs doesn't support IMCR, so this doesn't run on Bochs. diff --git a/src/param.rs b/src/param.rs index 4341329..a28e84c 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,2 +1,2 @@ -pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack +// pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack (unused in p3) pub const NCPU: usize = 8; // maximum number of CPUs diff --git a/src/x86.rs b/src/x86.rs index ba0598c..6028974 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -25,18 +25,19 @@ pub fn outb(port: u16, value: u8) { } } -pub fn inw(port: u16) -> u16 { - let result: u16; - unsafe { - asm!( - "in ax, dx", - in("dx") port, - out("ax") result, - options(nomem, nostack) - ); - result - } -} +// Unused in p3 - needed for later phases +// pub fn inw(port: u16) -> u16 { +// let result: u16; +// unsafe { +// asm!( +// "in ax, dx", +// in("dx") port, +// out("ax") result, +// options(nomem, nostack) +// ); +// result +// } +// } pub fn outw(port: u16, value: u16) { unsafe { @@ -49,27 +50,38 @@ pub fn outw(port: u16, value: u16) { } } -pub fn inl(port: u16) -> u32 { - unsafe { - let result: u32; - asm!( - "in eax, dx", - in("dx") port, - out("eax") result, - options(nomem, nostack) - ); - result - } -} - -pub fn outl(port: u16, value: u32) { - unsafe { - asm!( - "out dx, eax", - in("dx") port, - in("eax") value, - options(nomem, nostack) - ); +// Unused in p3 - needed for later phases +// pub fn inl(port: u16) -> u32 { +// unsafe { +// let result: u32; +// asm!( +// "in eax, dx", +// in("dx") port, +// out("eax") result, +// options(nomem, nostack) +// ); +// result +// } +// } + +// pub fn outl(port: u16, value: u32) { +// unsafe { +// asm!( +// "out dx, eax", +// in("dx") port, +// in("eax") value, +// options(nomem, nostack) +// ); +// } +// } + +/// Disable interrupts +/// +/// Clear the interrupt flag (IF) in EFLAGS to prevent the processor +/// from responding to maskable hardware interrupts. +pub fn cli() { + unsafe { + asm!("cli", options(nomem, nostack)); } } From cf5aed903799431961cbbbf11cf5307e99b922b8 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sun, 1 Mar 2026 04:45:46 +0000 Subject: [PATCH 16/69] Refleted panic changes of p3-pic --- src/lib.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aa34772..91ff1d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,17 @@ macro_rules! println { }); } +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + // Disable interrupts to prevent interrupt handlers from interfering + cli(); + // Print panic message with LAPIC ID to identify which CPU panicked + println!("lapicid {}:\n{:#?}", lapicid(), info); + unsafe { PANICKED = true; } + // Halt the system + loop {} +} + fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { @@ -54,8 +65,3 @@ pub extern "C" fn entryofrust() -> ! { } } -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - println!("Kernel Panic: {:?}", info); - loop {} -} \ No newline at end of file From 9d6fa5ac83825c0a031427fcf05ff0fa0eda2035 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sun, 1 Mar 2026 04:50:41 +0000 Subject: [PATCH 17/69] Fixed Scope Issues --- src/lib.rs | 4 ++++ src/x86.rs | 49 ------------------------------------------------- 2 files changed, 4 insertions(+), 49 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 91ff1d9..7808508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![no_main] // No main function use core::panic::PanicInfo; +use crate::x86::cli; +use crate::lapic::lapicid; mod param; mod x86; @@ -27,6 +29,8 @@ macro_rules! println { }); } +static mut PANICKED: bool = false; + #[panic_handler] fn panic(info: &PanicInfo) -> ! { // Disable interrupts to prevent interrupt handlers from interfering diff --git a/src/x86.rs b/src/x86.rs index 6028974..3aab64b 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -25,20 +25,6 @@ pub fn outb(port: u16, value: u8) { } } -// Unused in p3 - needed for later phases -// pub fn inw(port: u16) -> u16 { -// let result: u16; -// unsafe { -// asm!( -// "in ax, dx", -// in("dx") port, -// out("ax") result, -// options(nomem, nostack) -// ); -// result -// } -// } - pub fn outw(port: u16, value: u16) { unsafe { asm!( @@ -50,41 +36,6 @@ pub fn outw(port: u16, value: u16) { } } -// Unused in p3 - needed for later phases -// pub fn inl(port: u16) -> u32 { -// unsafe { -// let result: u32; -// asm!( -// "in eax, dx", -// in("dx") port, -// out("eax") result, -// options(nomem, nostack) -// ); -// result -// } -// } - -// pub fn outl(port: u16, value: u32) { -// unsafe { -// asm!( -// "out dx, eax", -// in("dx") port, -// in("eax") value, -// options(nomem, nostack) -// ); -// } -// } - -/// Disable interrupts -/// -/// Clear the interrupt flag (IF) in EFLAGS to prevent the processor -/// from responding to maskable hardware interrupts. -pub fn cli() { - unsafe { - asm!("cli", options(nomem, nostack)); - } -} - pub fn readeflags() -> u32 { unsafe { let eflags: u32; From 09daea8a4796827f61bd4dc09e0f1b71b8931e02 Mon Sep 17 00:00:00 2001 From: Arinjay Singhal <137890548+ArinjaySinghal19@users.noreply.github.com> Date: Mon, 16 Feb 2026 17:19:34 +0530 Subject: [PATCH 18/69] p5: add UART interrupt and console input support --- Makefile | 31 +++++++--------------- src/console.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++----- src/lapic.rs | 4 +-- src/lib.rs | 2 +- src/traps.rs | 67 +++++++++++++++++++++++------------------------ src/uart.rs | 31 +++++++++++++++++++--- 6 files changed, 138 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 9e81b2f..0d99f98 100644 --- a/Makefile +++ b/Makefile @@ -16,18 +16,9 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d then echo ''; \ else echo "***" 1>&2; \ echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) + exit 1; fi) endif -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU ifndef QEMU QEMU = $(shell if which qemu > /dev/null; \ then echo qemu; exit; \ @@ -35,14 +26,7 @@ QEMU = $(shell if which qemu > /dev/null; \ then echo qemu-system-i386; exit; \ elif which qemu-system-x86_64 > /dev/null; \ then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) + else echo "*** Error: Couldn't find QEMU." 1>&2; exit 1; fi) endif CC = $(TOOLPREFIX)gcc @@ -50,12 +34,13 @@ AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump + CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) + ASFLAGS = -m32 -gdwarf-2 -Wa,-divide LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) -# Disable PIE when possible (for Ubuntu 16.10 toolchain) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) CFLAGS += -fno-pie -no-pie endif @@ -94,7 +79,7 @@ kernel.a: $(RS) 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 @@ -104,6 +89,7 @@ vectors.S: vectors.pl .PRECIOUS: %.o -include *.d +clean: clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S @@ -114,8 +100,9 @@ GDBPORT = $(shell expr `id -u` % 5000 + 25000) QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) + ifndef CPUS - CPUS := 1 +CPUS := 1 endif # Attach fs.img as disk1 (index=1), like the C version @@ -131,4 +118,4 @@ qemu: xv6.img fs.img qemu-gdb: xv6.img fs .gdbinit @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/console.rs b/src/console.rs index 12abba1..25bde2f 100644 --- a/src/console.rs +++ b/src/console.rs @@ -5,20 +5,79 @@ pub struct Console {} impl Write for Console { fn write_str(&mut self, s: &str) -> Result { for c in s.chars() { - consputc(c); + consputc(c as i32); } Ok(()) } } -const BACKSPACE: char = '\x08'; +const BACKSPACE: i32 = 0x100; +const INPUT_BUF: usize = 128; +const CTRL_D: i32 = C('D'); -pub fn consputc(c: char) { +#[allow(non_snake_case)] +const fn C(c: char) -> i32 { + (c as i32) - ('@' as i32) +} + +#[derive(Clone, Copy)] +struct Input { + buf: [u8; INPUT_BUF], + r: usize, + w: usize, + e: usize, +} + +static mut INPUT: Input = Input { + buf: [0; INPUT_BUF], + r: 0, + w: 0, + e: 0, +}; + +fn consputc(c: i32) { if c == BACKSPACE { - uartputc(BACKSPACE); - uartputc(' '); - uartputc(BACKSPACE); + uartputc('\x08' as i32); + uartputc(' ' as i32); + uartputc('\x08' as i32); } else { uartputc(c); } +} + +pub fn consoleintr(getc: fn() -> i32) { + loop { + let c = getc(); + if c < 0 { + break; + } + + let input = unsafe { &mut INPUT }; + + match c { + x if x == C('U') => { + while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { + input.e -= 1; + consputc(BACKSPACE); + } + } + x if x == C('H') || x == 0x7f => { + if input.e != input.w { + input.e -= 1; + consputc(BACKSPACE); + } + } + _ => { + if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + input.buf[input.e % INPUT_BUF] = c as u8; + input.e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { + input.w = input.e; + } + } + } + } + } } \ No newline at end of file diff --git a/src/lapic.rs b/src/lapic.rs index 1ce725f..1bafa51 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -1,6 +1,6 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::traps::{T_IRQ0, IRQ_TIMER, IRQ_SPURIOUS, IRQ_ERROR}; +use crate::constants::{IRQ_ERROR, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; const ID: isize = 0x0020 / 4; const VER: isize = 0x0030 / 4; @@ -70,7 +70,7 @@ pub fn lapicinit() { // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 1000000000); + lapicw(TICR, 10000000); // Disable logical interrupt lines. diff --git a/src/lib.rs b/src/lib.rs index 561ebcc..fa1cc42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,5 +83,5 @@ pub extern "C" fn entryofrust() -> ! { #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("Kernel Panic: {:?}", info); - loop {} + halt() } \ No newline at end of file diff --git a/src/traps.rs b/src/traps.rs index 6899856..4010605 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,27 +1,25 @@ use modular_bitfield::prelude::*; -use core::cell::{OnceCell, RefCell}; +use core::cell::OnceCell; +use core::sync::atomic::{AtomicU32, Ordering}; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt,rcr2}; use crate::lapic; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::uart::uartintr; const SEG_KCODE: u16 = 1; const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate const STS_TG32: u8 = 0xF; // 32-bit Trap Gate -pub const T_IRQ0: u32 = 32; -pub const IRQ_TIMER: u32 = 0; -pub const IRQ_ERROR: u32 = 19; -pub const IRQ_SPURIOUS: u32 = 31; - extern "C" { - static vectors: [usize; 256]; // remove assembly. + static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } #[bitfield] #[repr(C, packed)] -#[derive(Clone, Copy, Default)] // debug can be removed ? do we need to ? +#[derive(Clone, Copy, Default)] pub struct GateDesc { off_15_0: B16, // low 16 bits of offset in segment cs: B16, // code segment selector @@ -40,7 +38,7 @@ impl GateDesc { self.set_args(0); self.set_rsv1(0); let typ = if is_trap { STS_TG32 } else { STS_IG32 }; - self.set_r_type(typ); // for an interrupt gate, for example. + self.set_r_type(typ); self.set_s(0); self.set_dpl(dpl); self.set_p(1); @@ -49,13 +47,8 @@ impl GateDesc { } -#[repr(C)] -pub struct IDTOnce { - pub idt: OnceCell<[GateDesc; 256]>, - pub ticks: RefCell -} -unsafe impl Sync for IDTOnce {} -pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new(), ticks: RefCell::new(0) }; +static mut IDT: OnceCell<[GateDesc; 256]> = OnceCell::new(); +pub static TICKS: AtomicU32 = AtomicU32::new(0); #[repr(C)] @@ -90,17 +83,20 @@ pub fn tvinit() { let mut arr = [GateDesc::default(); 256]; for i in 0..256 { arr[i].set_gate( - false, // Use an interrupt gate. - SEG_KCODE << 3, // Code segment selector (shifted as in the C code). - unsafe { vectors[i] }, // Offset from the external vector table. - 0 // Descriptor privilege level. + false, + SEG_KCODE << 3, + unsafe { vectors[i] }, + 0 ); } - let _ = IDT.idt.set(arr); + unsafe { + let _ = IDT.set(arr); + } } pub fn idtinit() { - lidt(IDT.idt.get().unwrap() , core::mem::size_of::<[GateDesc; 256]>() as usize); + let idt = unsafe { IDT.get().expect("IDT not initialized") }; + lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } @@ -117,18 +113,21 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; match tf.trapno { - TIMER => { - *IDT.ticks.borrow_mut() += 1; - println!("Tick {}!", IDT.ticks.borrow()); - lapic::lapiceoi(); - } - SEVEN | SPURIOUS => { - println!( - "cpu{}: spurious interrupt at {}:{}\n", - cpuid() , - tf.cs, - tf.eip - ); + TIMER => { + TICKS.fetch_add(1, Ordering::Relaxed); + lapic::lapiceoi(); + } + x if x == T_IRQ0 + IRQ_COM1 => { + uartintr(); + lapiceoi(); + } + SEVEN | SPURIOUS => { + println!( + "cpu{}: spurious interrupt at {:x}:{:x}\n", + cpuid(), + tf.cs, + tf.eip + ); lapiceoi(); } crate::constants::IDE_TRAP => { diff --git a/src/uart.rs b/src/uart.rs index 9888088..aaf3137 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,13 @@ -use crate::x86::{outb, inb}; +use crate::console::consoleintr; +use crate::x86::{inb, outb}; use crate::ioapic::ioapic_enable; +use core::sync::atomic::{AtomicBool, Ordering}; + const COM1: u16 = 0x3F8; // COM1 port address pub const IRQ_COM1: u32 = 4; + +static UART_READY: AtomicBool = AtomicBool::new(false); + pub fn uartinit() { // Turn off the FIFO. outb(COM1 + 2, 0); @@ -19,6 +25,7 @@ pub fn uartinit() { if inb(COM1 + 5) == 0xFF { return; } + UART_READY.store(true, Ordering::SeqCst); // Acknowledge pre-existing interrupt conditions; // enable interrupts. @@ -28,15 +35,33 @@ pub fn uartinit() { // Announce that the UART is active. for p in "xv6...\n".chars() { - uartputc(p); + uartputc(p as i32); } } -pub fn uartputc(c: char) { +pub fn uartputc(c: i32) { + if !UART_READY.load(Ordering::SeqCst) { + return; + } + for _ in 0..128 { if inb(COM1 + 5) & 0x20 != 0 { break; } } outb(COM1 + 0, c as u8); +} + +fn uartgetc() -> i32 { + if !UART_READY.load(Ordering::SeqCst) { + return -1; + } + if (inb(COM1 + 5) & 0x01) == 0 { + return -1; + } + inb(COM1) as i32 +} + +pub fn uartintr() { + consoleintr(uartgetc); } \ No newline at end of file From c31ec4ab8fd5df8cec9c1fd7e7421024377590af Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 14:54:32 +0000 Subject: [PATCH 19/69] Updated console::consputc function call API --- src/console.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/console.rs b/src/console.rs index 25bde2f..59f186a 100644 --- a/src/console.rs +++ b/src/console.rs @@ -35,7 +35,7 @@ static mut INPUT: Input = Input { e: 0, }; -fn consputc(c: i32) { +pub fn consputc(c: i32) { if c == BACKSPACE { uartputc('\x08' as i32); uartputc(' ' as i32); diff --git a/src/lib.rs b/src/lib.rs index fa1cc42..4421108 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ fn welcome() { for &byte in data0.iter() { if byte == 0 { break; } - console::consputc(byte as char); + console::consputc(byte as i32); } bio::brelse(b0); From e21d58fc74ec01570ce9899ebe03bf4a636fad56 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 04:28:43 +0000 Subject: [PATCH 20/69] Updated project level config.toml --- .cargo/config.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0953166 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "targets/i686.json" + +[unstable] +build-std = ["core"] +build-std-features = ["compiler-builtins-mem"] From 09318fbf1c0eb5627c7e452d5a8f91142a5d5601 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 04:47:41 +0000 Subject: [PATCH 21/69] Accumulated older changes --- Cargo.toml | 1 + src/lib.rs | 13 +++++++++++-- src/mp.rs | 12 ++++++++---- src/traps.rs | 6 +----- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2dd5beb..6c2171c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "xv6" version = "0.1.0" edition = "2021" +autobins = false [lib] name = "kernel" diff --git a/src/lib.rs b/src/lib.rs index 4421108..b8c2f0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![no_main] // No main function use core::panic::PanicInfo; +use crate::x86::cli; +use crate::lapic::lapicid; mod param; mod x86; @@ -80,8 +82,15 @@ pub extern "C" fn entryofrust() -> ! { } } +static mut PANICKED: bool = false; + #[panic_handler] fn panic(info: &PanicInfo) -> ! { - println!("Kernel Panic: {:?}", info); - halt() + // Disable interrupts to prevent interrupt handlers from interfering + cli(); + // Print panic message with LAPIC ID to identify which CPU panicked + println!("lapicid {}:\n{:#?}", lapicid(), info); + unsafe { PANICKED = true; } + // Halt the system + loop {} } \ No newline at end of file diff --git a/src/mp.rs b/src/mp.rs index 9684f51..f291c43 100644 --- a/src/mp.rs +++ b/src/mp.rs @@ -4,7 +4,8 @@ use crate::x86::{outb, inb}; use crate::proc::Cpu; use core::cell::OnceCell; -pub static mut IOAPICID: u8 = 0; +// Removed: replaced by MP_ONCE.ioapic_id OnceCell pattern +// pub static mut IOAPICID: u8 = 0; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -194,7 +195,8 @@ pub fn mpinit() { let conf = unsafe { *(mp.physaddr as *mut MpConf) }; let mut ismp = true; - MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32); + MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32) + .expect("lapic_base already initialized"); let mut p = (mp.physaddr as usize + mem::size_of::()) as *const u8; let e = (mp.physaddr as usize + conf.length as usize) as *const u8; @@ -218,7 +220,8 @@ pub fn mpinit() { MPIOAPIC => { let ioapic = p as *const MpIoApic; let ioapicid = unsafe { (*ioapic).apicno }; - MP_ONCE.ioapic_id.set(ioapicid); + MP_ONCE.ioapic_id.set(ioapicid) + .expect("ioapic_id already initialized"); // p = unsafe{ p.add(mem::size_of::()) }; p = p.wrapping_add(mem::size_of::()); } @@ -236,7 +239,8 @@ pub fn mpinit() { if !ismp { panic!("Didn't find a suitable machine"); } - MP_ONCE.cpus.set(cpus); + MP_ONCE.cpus.set(cpus) + .expect("cpus array already initialized"); if mp.imcrp != 0 { // Bochs doesn't support IMCR, so this doesn't run on Bochs. diff --git a/src/traps.rs b/src/traps.rs index 4010605..4974746 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -6,13 +6,9 @@ use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt,rcr2}; use crate::lapic; -use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0, SEG_KCODE, STS_IG32, STS_TG32}; use crate::uart::uartintr; -const SEG_KCODE: u16 = 1; -const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate -const STS_TG32: u8 = 0xF; // 32-bit Trap Gate - extern "C" { static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } From b6642c8302b606bf2de8ab8983d03c107cf99b18 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 05:15:11 +0000 Subject: [PATCH 22/69] Fixed static mutable warnings --- src/console.rs | 43 ++++++++++++++++++++++--------------------- src/ioapic.rs | 4 ++-- src/lib.rs | 2 ++ src/proc.rs | 2 +- src/traps.rs | 5 +++-- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/console.rs b/src/console.rs index 59f186a..61f0aa3 100644 --- a/src/console.rs +++ b/src/console.rs @@ -52,29 +52,30 @@ pub fn consoleintr(getc: fn() -> i32) { break; } - let input = unsafe { &mut INPUT }; - - match c { - x if x == C('U') => { - while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { - input.e -= 1; - consputc(BACKSPACE); + unsafe { + let input = &raw mut INPUT; + match c { + x if x == C('U') => { + while (*input).e != (*input).w && (*input).buf[((*input).e - 1) % INPUT_BUF] != b'\n' { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - x if x == C('H') || x == 0x7f => { - if input.e != input.w { - input.e -= 1; - consputc(BACKSPACE); + x if x == C('H') || x == 0x7f => { + if (*input).e != (*input).w { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - _ => { - if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { - let c = if c == '\r' as i32 { '\n' as i32 } else { c }; - input.buf[input.e % INPUT_BUF] = c as u8; - input.e += 1; - consputc(c); - if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { - input.w = input.e; + _ => { + if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + (*input).buf[(*input).e % INPUT_BUF] = c as u8; + (*input).e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { + (*input).w = (*input).e; + } } } } diff --git a/src/ioapic.rs b/src/ioapic.rs index 15cf837..979cfd0 100644 --- a/src/ioapic.rs +++ b/src/ioapic.rs @@ -1,7 +1,7 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::console::Console; -use core::fmt::Write; +// use crate::console::Console; +// use core::fmt::Write; use crate::println; // I/O APIC default physical address diff --git a/src/lib.rs b/src/lib.rs index b8c2f0a..9727da0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] // No standard library #![no_main] // No main function +#![allow(dead_code)] use core::panic::PanicInfo; use crate::x86::cli; @@ -59,6 +60,7 @@ fn welcome() { bio::brelse(b1); } +// alltraps is an assembly label not a static function pointer in Rust extern "C" { pub fn alltraps(); } diff --git a/src/proc.rs b/src/proc.rs index 279364c..713ad58 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,5 +1,5 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs -use core::ptr; +// use core::ptr; use crate::x86::readeflags; use crate::constants::{FL_IF,NCPU}; use crate::lapic; diff --git a/src/traps.rs b/src/traps.rs index 4974746..4806d32 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,6 +1,7 @@ use modular_bitfield::prelude::*; use core::cell::OnceCell; use core::sync::atomic::{AtomicU32, Ordering}; +use core::ptr::addr_of_mut; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; @@ -86,12 +87,12 @@ pub fn tvinit() { ); } unsafe { - let _ = IDT.set(arr); + let _ = (*addr_of_mut!(IDT)).set(arr); } } pub fn idtinit() { - let idt = unsafe { IDT.get().expect("IDT not initialized") }; + let idt = unsafe { (*addr_of_mut!(IDT)).get().expect("IDT not initialized") }; lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } From ac785d11d0bc5d2f932c614d900e893dbf31a8b4 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 16:25:12 +0000 Subject: [PATCH 23/69] p6: Fixed iderw to use interrupt-driven design --- Makefile | 1 - src/ide.rs | 40 +++++++++++++++++++++++++--------------- src/lib.rs | 4 ++-- src/x86.rs | 27 ++++++++++++++------------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 0d99f98..6e9abeb 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,6 @@ vectors.S: vectors.pl .PRECIOUS: %.o -include *.d -clean: clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S diff --git a/src/ide.rs b/src/ide.rs index 4ae4ee2..180b78e 100644 --- a/src/ide.rs +++ b/src/ide.rs @@ -133,7 +133,9 @@ pub fn ideintr() { } } -// ide.rs +// Sync buf with disk. +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. pub fn iderw(idx: usize) { let b = crate::bio::buf_mut(idx); @@ -148,23 +150,31 @@ pub fn iderw(idx: usize) { } } - // PURE POLLING: issue the command directly (no IDEQUEUE). - idestart(idx); - - // Wait for completion. - if idewait(true) < 0 { - panic!("iderw: ide error"); + // Append b to idequeue. + b.qnext = None; + unsafe { + let mut pp: *mut Option = &raw mut IDEQUEUE; + while let Some(next_idx) = *pp { + pp = &raw mut crate::bio::buf_mut(next_idx).qnext; + } + *pp = Some(idx); } - // If it was a read, pull data now. - let flags_now = b.flags.load(Ordering::Acquire); - if (flags_now & B_DIRTY) == 0 { - unsafe { - crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + // Start disk if necessary. + unsafe { + if IDEQUEUE == Some(idx) { + idestart(idx); } } - // If it was a write, data was already pushed in idestart() via outsl(). - b.flags.fetch_or(B_VALID, Ordering::AcqRel); - b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + // Wait for request to finish. + // The interrupt handler will set B_VALID when done. + loop { + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + break; + } + // Force compiler to re-read b->flags which is modified by ideintr() + x86::noop(); + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9727da0..21209db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,8 @@ macro_rules! println { fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { - x86::outw(0x604, 0x2000); - x86::outw(0xB004, 0x2000); + x86::outw(0x602, 0x2000); // QEMU isa-debug-exit device + x86::outw(0xB002, 0x2000); // VirtualBox shutdown port } } diff --git a/src/x86.rs b/src/x86.rs index fb341ef..ba0db57 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -129,22 +129,23 @@ pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { } - +// Output string of dwords to port using rep outsd instruction. +// Matches the C implementation: outsl(port, addr, cnt) +// Note: ESI must be saved/restored as LLVM restricts its use in 32-bit mode pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { - let mut p = addr; - for _ in 0..cnt { - let val = core::ptr::read(p); - asm!( - "out dx, eax", - in("dx") port, - in("eax") val, - options(nomem, nostack, preserves_flags), - ); - p = p.add(1); - } + let addr_val = addr as u32; + core::arch::asm!( + "push esi", + "mov esi, {addr}", + "cld", + "rep outsd", + "pop esi", + addr = in(reg) addr_val, + in("dx") port, + inout("ecx") cnt => _, + ); } - /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state From 4feb5666572917fb35f241ff091d6b45c9c9a5e2 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 16:39:04 +0000 Subject: [PATCH 24/69] Updated port exit status --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 21209db..b714a15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,8 @@ macro_rules! println { fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { - x86::outw(0x602, 0x2000); // QEMU isa-debug-exit device - x86::outw(0xB002, 0x2000); // VirtualBox shutdown port + x86::outw(0x604, 0x2000); // QEMU isa-debug-exit device + x86::outw(0xB004, 0x2000); // VirtualBox shutdown port } } From d2aa22a35799ef9f7386e047fa3596fb3a7545e0 Mon Sep 17 00:00:00 2001 From: Naman Garg Date: Mon, 9 Feb 2026 17:33:54 +0530 Subject: [PATCH 25/69] Initial commit From b2ebc350d2249378780137bb1ca9d80c9326a085 Mon Sep 17 00:00:00 2001 From: ahilaan Date: Sat, 14 Feb 2026 07:51:10 +0000 Subject: [PATCH 26/69] p6: add IDE driver + buffer cache --- Makefile | 30 +++++---- src/bio.rs | 148 +++++++++++++++++++++++++++++++++++++++++ src/buf.rs | 39 +++++++++++ src/constants.rs | 7 +- src/ide.rs | 170 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 30 ++++++++- src/traps.rs | 8 ++- src/x86.rs | 33 +++++++++ welcome.txt | 8 +++ 9 files changed, 451 insertions(+), 22 deletions(-) create mode 100644 src/bio.rs create mode 100644 src/buf.rs create mode 100644 src/ide.rs create mode 100644 welcome.txt diff --git a/Makefile b/Makefile index 34b0be2..61d8093 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ RS = src/*.rs #TOOLPREFIX = i386-elf- # Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = +#TOOLPREFIX = # Try to infer the correct TOOLPREFIX if not set ifndef TOOLPREFIX @@ -53,7 +53,6 @@ OBJDUMP = $(TOOLPREFIX)objdump CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -# FreeBSD ld wants ``elf_i386_fbsd'' LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) # Disable PIE when possible (for Ubuntu 16.10 toolchain) @@ -64,6 +63,7 @@ ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) CFLAGS += -fno-pie -nopie endif +# Disk image with bootblock + kernel xv6.img: bootblock kernel dd if=/dev/zero of=xv6.img count=10000 dd if=bootblock of=xv6.img conv=notrunc @@ -84,7 +84,13 @@ bootblock: bootasm.S bootmain.c ./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 build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ + --target ./targets/i686.json --release + @tdir=$$(cargo metadata --format-version=1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p'); \ + lib=$$(find "$$tdir" -maxdepth 4 -type f -name 'libkernel.a' | head -n 1); \ + if [ -z "$$lib" ]; then echo "ERROR: libkernel.a not found"; exit 1; fi; \ + cp "$$lib" kernel.a + kernel: kernel.a $(OBJS) ./linkers/kernel.ld ld -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a @@ -102,18 +108,15 @@ vectors.S: vectors.pl # details: # http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html .PRECIOUS: %.o - -include *.d -clean: +clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img mkfs .gdbinit vectors.S - rm -r target + rm -rf target # run in emulators -# try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) -# QEMU's gdb stub command line changed in 0.11 QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) @@ -121,16 +124,17 @@ ifndef CPUS CPUS := 1 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) +# Attach fs.img as disk1 (index=1), like the C version +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 fs .gdbinit @echo "*** Now run 'gdb'." 1>&2 $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file diff --git a/src/bio.rs b/src/bio.rs new file mode 100644 index 0000000..a9ceb74 --- /dev/null +++ b/src/bio.rs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/buf.rs b/src/buf.rs new file mode 100644 index 0000000..2c4cfb2 --- /dev/null +++ b/src/buf.rs @@ -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, + + 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], + } + } +} \ No newline at end of file diff --git a/src/constants.rs b/src/constants.rs index d06b826..b36418c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -51,17 +51,14 @@ pub const T_SIMDERR: u32 = 19; // SIMD floating point error // processor defined exceptions or interrupt vectors pub const T_SYSCALL: u32 = 64; // system call pub const T_DEFAULT: u32 = 500; // catchall - -pub const T_IRQ0: u32 = 32; // IRQ 0 corresponds to int T_IRQ - +pub const T_IRQ0: u32 = 32; pub const IRQ_TIMER: u32 = 0; pub const IRQ_KBD: u32 = 1; pub const IRQ_COM1: u32 = 4; pub const IRQ_IDE: u32 = 14; pub const IRQ_ERROR: u32 = 19; pub const IRQ_SPURIOUS: u32 = 31; - - +pub const IDE_TRAP: u32 = T_IRQ0 + IRQ_IDE; // ------------------------------------------------------ MP RELATED ------------------------------------------------------- // Processor flags diff --git a/src/ide.rs b/src/ide.rs new file mode 100644 index 0000000..4ae4ee2 --- /dev/null +++ b/src/ide.rs @@ -0,0 +1,170 @@ +use core::sync::atomic::Ordering; +use crate::buf::{BSIZE, B_DIRTY, B_VALID}; +use crate::x86; + +const SECTOR_SIZE: usize = 512; + +const IDE_BSY: u8 = 0x80; +const IDE_DRDY: u8 = 0x40; +const IDE_DF: u8 = 0x20; +const IDE_ERR: u8 = 0x01; + +const IDE_CMD_READ: u8 = 0x20; +const IDE_CMD_WRITE: u8 = 0x30; +const IDE_CMD_RDMUL: u8 = 0xC4; +const IDE_CMD_WRMUL: u8 = 0xC5; + +// If you later build a real FS image, keep this consistent with your mkfs. +// xv6 uses 1000. +const FSSIZE: u32 = 1000; + +static mut IDEQUEUE: Option = None; +static mut HAVEDISK1: bool = false; + +fn idewait(checkerr: bool) -> i32 { + let mut r: u8; + loop { + r = x86::inb(0x1F7); + if (r & (IDE_BSY | IDE_DRDY)) == IDE_DRDY { + break; + } + } + if checkerr && (r & (IDE_DF | IDE_ERR)) != 0 { + return -1; + } + 0 +} + +pub fn ideinit() { + // Route IDE IRQ somewhere; simplest is CPU 0 for now. + crate::ioapic::ioapic_enable(crate::constants::IRQ_IDE, 0); + + idewait(false); + + // Check if disk 1 is present + unsafe { + x86::outb(0x1F6, 0xE0 | (1 << 4)); + for _ in 0..1000 { + if x86::inb(0x1F7) != 0 { + HAVEDISK1 = true; + break; + } + } + // Switch back to disk 0 + x86::outb(0x1F6, 0xE0 | (0 << 4)); + } +} + +fn idestart(idx: usize) { + let b = crate::bio::buf_mut(idx); + + if b.blockno >= FSSIZE { + panic!("idestart: incorrect blockno"); + } + + let sector_per_block = BSIZE / SECTOR_SIZE; // usually 1 + let sector = (b.blockno as usize) * sector_per_block; + + let read_cmd = if sector_per_block == 1 { IDE_CMD_READ } else { IDE_CMD_RDMUL }; + let write_cmd = if sector_per_block == 1 { IDE_CMD_WRITE } else { IDE_CMD_WRMUL }; + + if sector_per_block > 7 { + panic!("idestart: sector_per_block > 7"); + } + + idewait(false); + x86::outb(0x3F6, 0); // generate interrupt + + x86::outb(0x1F2, sector_per_block as u8); // number of sectors + x86::outb(0x1F3, (sector & 0xFF) as u8); + x86::outb(0x1F4, ((sector >> 8) & 0xFF) as u8); + x86::outb(0x1F5, ((sector >> 16) & 0xFF) as u8); + x86::outb( + 0x1F6, + 0xE0 | (((b.dev & 1) as u8) << 4) | (((sector >> 24) & 0x0F) as u8), + ); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & B_DIRTY) != 0 { + x86::outb(0x1F7, write_cmd); + + // write BSIZE bytes as u32 words + unsafe { + x86::outsl(0x1F0, b.data.as_ptr() as *const u32, BSIZE / 4); + } + } else { + x86::outb(0x1F7, read_cmd); + } +} + +// Interrupt handler. +pub fn ideintr() { + let idx = unsafe { + match IDEQUEUE { + None => return, + Some(i) => { + let b = crate::bio::buf_mut(i); + IDEQUEUE = b.qnext; + b.qnext = None; + i + } + } + }; + + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + + // Read data if needed. + if (flags & B_DIRTY) == 0 && idewait(true) >= 0 { + unsafe { + x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + + // Start next buffer in queue. + unsafe { + if let Some(next) = IDEQUEUE { + idestart(next); + } + } +} + +// ide.rs +pub fn iderw(idx: usize) { + let b = crate::bio::buf_mut(idx); + + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + panic!("iderw: nothing to do"); + } + + unsafe { + if b.dev != 0 && !HAVEDISK1 { + panic!("iderw: ide disk 1 not present"); + } + } + + // PURE POLLING: issue the command directly (no IDEQUEUE). + idestart(idx); + + // Wait for completion. + if idewait(true) < 0 { + panic!("iderw: ide error"); + } + + // If it was a read, pull data now. + let flags_now = b.flags.load(Ordering::Acquire); + if (flags_now & B_DIRTY) == 0 { + unsafe { + crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + } + } + // If it was a write, data was already pushed in idestart() via outsl(). + + b.flags.fetch_or(B_VALID, Ordering::AcqRel); + b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aa34772..561ebcc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,9 @@ mod mp; mod proc; mod traps; mod constants; - +mod buf; +mod bio; +mod ide; use crate::traps::*; #[macro_export] @@ -35,8 +37,28 @@ fn halt() -> ! { } } +fn welcome() { + let b0 = bio::bread(1, 0); + let data0 = bio::buf_mut(b0).data; + + for &byte in data0.iter() { + if byte == 0 { break; } + console::consputc(byte as char); + } + bio::brelse(b0); + + let b1 = bio::bread(1, 1); + let count = bio::buf_mut(b1).data[0]; + + println!("\nAfter preparing fs.img, we have rebooted {} times\n", count); + + bio::buf_mut(b1).data[0] = count.wrapping_add(1); + bio::bwrite(b1); + bio::brelse(b1); +} + extern "C" { - pub static alltraps: fn(); + pub fn alltraps(); } #[no_mangle] @@ -46,9 +68,13 @@ pub extern "C" fn entryofrust() -> ! { picirq::picinit(); ioapic::ioapic_init(); uart::uartinit(); + ide::ideinit(); tvinit(); + bio::binit(); idtinit(); x86::sti(); + welcome(); + loop { x86::wfi(); } diff --git a/src/traps.rs b/src/traps.rs index 0643fcc..6899856 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -96,7 +96,7 @@ pub fn tvinit() { 0 // Descriptor privilege level. ); } - IDT.idt.set(arr); + let _ = IDT.idt.set(arr); } pub fn idtinit() { @@ -116,7 +116,6 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const TIMER: u32 = T_IRQ0 + IRQ_TIMER; const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; - match tf.trapno { TIMER => { *IDT.ticks.borrow_mut() += 1; @@ -132,6 +131,11 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { ); lapiceoi(); } + crate::constants::IDE_TRAP => { + crate::ide::ideintr(); + crate::lapic::lapiceoi(); + } + _ => { println!( "unexpected trap {} from cpu {} eip {} (cr2=0x{:x})\n", diff --git a/src/x86.rs b/src/x86.rs index ba0598c..fb341ef 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -112,6 +112,39 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn noop() { + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); +} + +// x86.rs +pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { + core::arch::asm!( + "cld", + "rep insd", + in("dx") port, + inout("edi") (addr as usize) => _, + inout("ecx") cnt => _, + options(nostack, preserves_flags), + ); +} + + + +pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { + let mut p = addr; + for _ in 0..cnt { + let val = core::ptr::read(p); + asm!( + "out dx, eax", + in("dx") port, + in("eax") val, + options(nomem, nostack, preserves_flags), + ); + p = p.add(1); + } +} + + /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state diff --git a/welcome.txt b/welcome.txt new file mode 100644 index 0000000..3ab143e --- /dev/null +++ b/welcome.txt @@ -0,0 +1,8 @@ + ### + # # ###### # #### #### # # ###### ### + # # # # # # # # ## ## # ### + # # ##### # # # # # ## # ##### # + # ## # # # # # # # # # + ## ## # # # # # # # # # ### + # # ###### ###### #### #### # # ###### ### + From e32b47b11454c8663b04febff11fabf701992b20 Mon Sep 17 00:00:00 2001 From: ahilaan Date: Sat, 14 Feb 2026 08:13:05 +0000 Subject: [PATCH 27/69] p6: add IDE driver and buffer cache layer --- src/console.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/console.rs b/src/console.rs index 0f62712..12abba1 100644 --- a/src/console.rs +++ b/src/console.rs @@ -13,7 +13,7 @@ impl Write for Console { const BACKSPACE: char = '\x08'; -fn consputc(c: char) { +pub fn consputc(c: char) { if c == BACKSPACE { uartputc(BACKSPACE); uartputc(' '); From 4ced3ca36c0d01df4155f6750be54be0655825d5 Mon Sep 17 00:00:00 2001 From: Nipun Goel Date: Sat, 14 Feb 2026 15:29:13 +0530 Subject: [PATCH 28/69] mkfs implemented --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 61d8093..54a83fa 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,12 @@ mkfs: src/mkfs.rs fs.img: mkfs *.txt ./mkfs fs.img *.txt +mkfs: src/mkfs.rs + rustc -W warnings -o mkfs src/mkfs.rs + +fs.img: mkfs *.txt + ./mkfs fs.img *.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 From a0f29fbcfc2d47fbfb570309d170ba2950bd4162 Mon Sep 17 00:00:00 2001 From: Nipun Goel Date: Sat, 14 Feb 2026 17:21:17 +0530 Subject: [PATCH 29/69] mkfs implemented From 6cf347606051955f0896a203e55fef19b9fb5e24 Mon Sep 17 00:00:00 2001 From: Arinjay Singhal <137890548+ArinjaySinghal19@users.noreply.github.com> Date: Mon, 16 Feb 2026 17:19:34 +0530 Subject: [PATCH 30/69] p5: add UART interrupt and console input support --- Makefile | 31 +++++++--------------- src/console.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++----- src/lapic.rs | 4 +-- src/lib.rs | 2 +- src/traps.rs | 67 +++++++++++++++++++++++------------------------ src/uart.rs | 31 +++++++++++++++++++--- 6 files changed, 138 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 54a83fa..884fb32 100644 --- a/Makefile +++ b/Makefile @@ -16,18 +16,9 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d then echo ''; \ else echo "***" 1>&2; \ echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) + exit 1; fi) endif -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU ifndef QEMU QEMU = $(shell if which qemu > /dev/null; \ then echo qemu; exit; \ @@ -35,14 +26,7 @@ QEMU = $(shell if which qemu > /dev/null; \ then echo qemu-system-i386; exit; \ elif which qemu-system-x86_64 > /dev/null; \ then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) + else echo "*** Error: Couldn't find QEMU." 1>&2; exit 1; fi) endif CC = $(TOOLPREFIX)gcc @@ -50,12 +34,13 @@ AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump + CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) + ASFLAGS = -m32 -gdwarf-2 -Wa,-divide LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) -# Disable PIE when possible (for Ubuntu 16.10 toolchain) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) CFLAGS += -fno-pie -no-pie endif @@ -99,7 +84,7 @@ kernel.a: $(RS) 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 @@ -116,6 +101,7 @@ vectors.S: vectors.pl .PRECIOUS: %.o -include *.d +clean: clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img mkfs .gdbinit vectors.S @@ -126,8 +112,9 @@ GDBPORT = $(shell expr `id -u` % 5000 + 25000) QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) + ifndef CPUS - CPUS := 1 +CPUS := 1 endif # Attach fs.img as disk1 (index=1), like the C version @@ -143,4 +130,4 @@ qemu: xv6.img fs.img qemu-gdb: xv6.img fs .gdbinit @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/console.rs b/src/console.rs index 12abba1..25bde2f 100644 --- a/src/console.rs +++ b/src/console.rs @@ -5,20 +5,79 @@ pub struct Console {} impl Write for Console { fn write_str(&mut self, s: &str) -> Result { for c in s.chars() { - consputc(c); + consputc(c as i32); } Ok(()) } } -const BACKSPACE: char = '\x08'; +const BACKSPACE: i32 = 0x100; +const INPUT_BUF: usize = 128; +const CTRL_D: i32 = C('D'); -pub fn consputc(c: char) { +#[allow(non_snake_case)] +const fn C(c: char) -> i32 { + (c as i32) - ('@' as i32) +} + +#[derive(Clone, Copy)] +struct Input { + buf: [u8; INPUT_BUF], + r: usize, + w: usize, + e: usize, +} + +static mut INPUT: Input = Input { + buf: [0; INPUT_BUF], + r: 0, + w: 0, + e: 0, +}; + +fn consputc(c: i32) { if c == BACKSPACE { - uartputc(BACKSPACE); - uartputc(' '); - uartputc(BACKSPACE); + uartputc('\x08' as i32); + uartputc(' ' as i32); + uartputc('\x08' as i32); } else { uartputc(c); } +} + +pub fn consoleintr(getc: fn() -> i32) { + loop { + let c = getc(); + if c < 0 { + break; + } + + let input = unsafe { &mut INPUT }; + + match c { + x if x == C('U') => { + while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { + input.e -= 1; + consputc(BACKSPACE); + } + } + x if x == C('H') || x == 0x7f => { + if input.e != input.w { + input.e -= 1; + consputc(BACKSPACE); + } + } + _ => { + if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + input.buf[input.e % INPUT_BUF] = c as u8; + input.e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { + input.w = input.e; + } + } + } + } + } } \ No newline at end of file diff --git a/src/lapic.rs b/src/lapic.rs index 1ce725f..1bafa51 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -1,6 +1,6 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::traps::{T_IRQ0, IRQ_TIMER, IRQ_SPURIOUS, IRQ_ERROR}; +use crate::constants::{IRQ_ERROR, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; const ID: isize = 0x0020 / 4; const VER: isize = 0x0030 / 4; @@ -70,7 +70,7 @@ pub fn lapicinit() { // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 1000000000); + lapicw(TICR, 10000000); // Disable logical interrupt lines. diff --git a/src/lib.rs b/src/lib.rs index 561ebcc..fa1cc42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,5 +83,5 @@ pub extern "C" fn entryofrust() -> ! { #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("Kernel Panic: {:?}", info); - loop {} + halt() } \ No newline at end of file diff --git a/src/traps.rs b/src/traps.rs index 6899856..4010605 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,27 +1,25 @@ use modular_bitfield::prelude::*; -use core::cell::{OnceCell, RefCell}; +use core::cell::OnceCell; +use core::sync::atomic::{AtomicU32, Ordering}; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt,rcr2}; use crate::lapic; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::uart::uartintr; const SEG_KCODE: u16 = 1; const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate const STS_TG32: u8 = 0xF; // 32-bit Trap Gate -pub const T_IRQ0: u32 = 32; -pub const IRQ_TIMER: u32 = 0; -pub const IRQ_ERROR: u32 = 19; -pub const IRQ_SPURIOUS: u32 = 31; - extern "C" { - static vectors: [usize; 256]; // remove assembly. + static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } #[bitfield] #[repr(C, packed)] -#[derive(Clone, Copy, Default)] // debug can be removed ? do we need to ? +#[derive(Clone, Copy, Default)] pub struct GateDesc { off_15_0: B16, // low 16 bits of offset in segment cs: B16, // code segment selector @@ -40,7 +38,7 @@ impl GateDesc { self.set_args(0); self.set_rsv1(0); let typ = if is_trap { STS_TG32 } else { STS_IG32 }; - self.set_r_type(typ); // for an interrupt gate, for example. + self.set_r_type(typ); self.set_s(0); self.set_dpl(dpl); self.set_p(1); @@ -49,13 +47,8 @@ impl GateDesc { } -#[repr(C)] -pub struct IDTOnce { - pub idt: OnceCell<[GateDesc; 256]>, - pub ticks: RefCell -} -unsafe impl Sync for IDTOnce {} -pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new(), ticks: RefCell::new(0) }; +static mut IDT: OnceCell<[GateDesc; 256]> = OnceCell::new(); +pub static TICKS: AtomicU32 = AtomicU32::new(0); #[repr(C)] @@ -90,17 +83,20 @@ pub fn tvinit() { let mut arr = [GateDesc::default(); 256]; for i in 0..256 { arr[i].set_gate( - false, // Use an interrupt gate. - SEG_KCODE << 3, // Code segment selector (shifted as in the C code). - unsafe { vectors[i] }, // Offset from the external vector table. - 0 // Descriptor privilege level. + false, + SEG_KCODE << 3, + unsafe { vectors[i] }, + 0 ); } - let _ = IDT.idt.set(arr); + unsafe { + let _ = IDT.set(arr); + } } pub fn idtinit() { - lidt(IDT.idt.get().unwrap() , core::mem::size_of::<[GateDesc; 256]>() as usize); + let idt = unsafe { IDT.get().expect("IDT not initialized") }; + lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } @@ -117,18 +113,21 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; const SEVEN: u32 = T_IRQ0 + 7; match tf.trapno { - TIMER => { - *IDT.ticks.borrow_mut() += 1; - println!("Tick {}!", IDT.ticks.borrow()); - lapic::lapiceoi(); - } - SEVEN | SPURIOUS => { - println!( - "cpu{}: spurious interrupt at {}:{}\n", - cpuid() , - tf.cs, - tf.eip - ); + TIMER => { + TICKS.fetch_add(1, Ordering::Relaxed); + lapic::lapiceoi(); + } + x if x == T_IRQ0 + IRQ_COM1 => { + uartintr(); + lapiceoi(); + } + SEVEN | SPURIOUS => { + println!( + "cpu{}: spurious interrupt at {:x}:{:x}\n", + cpuid(), + tf.cs, + tf.eip + ); lapiceoi(); } crate::constants::IDE_TRAP => { diff --git a/src/uart.rs b/src/uart.rs index 9888088..aaf3137 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,13 @@ -use crate::x86::{outb, inb}; +use crate::console::consoleintr; +use crate::x86::{inb, outb}; use crate::ioapic::ioapic_enable; +use core::sync::atomic::{AtomicBool, Ordering}; + const COM1: u16 = 0x3F8; // COM1 port address pub const IRQ_COM1: u32 = 4; + +static UART_READY: AtomicBool = AtomicBool::new(false); + pub fn uartinit() { // Turn off the FIFO. outb(COM1 + 2, 0); @@ -19,6 +25,7 @@ pub fn uartinit() { if inb(COM1 + 5) == 0xFF { return; } + UART_READY.store(true, Ordering::SeqCst); // Acknowledge pre-existing interrupt conditions; // enable interrupts. @@ -28,15 +35,33 @@ pub fn uartinit() { // Announce that the UART is active. for p in "xv6...\n".chars() { - uartputc(p); + uartputc(p as i32); } } -pub fn uartputc(c: char) { +pub fn uartputc(c: i32) { + if !UART_READY.load(Ordering::SeqCst) { + return; + } + for _ in 0..128 { if inb(COM1 + 5) & 0x20 != 0 { break; } } outb(COM1 + 0, c as u8); +} + +fn uartgetc() -> i32 { + if !UART_READY.load(Ordering::SeqCst) { + return -1; + } + if (inb(COM1 + 5) & 0x01) == 0 { + return -1; + } + inb(COM1) as i32 +} + +pub fn uartintr() { + consoleintr(uartgetc); } \ No newline at end of file From 2f4af2ff262e628d4ef607f59e37c248bf17a0b1 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 14:54:32 +0000 Subject: [PATCH 31/69] Updated console::consputc function call API --- src/console.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/console.rs b/src/console.rs index 25bde2f..59f186a 100644 --- a/src/console.rs +++ b/src/console.rs @@ -35,7 +35,7 @@ static mut INPUT: Input = Input { e: 0, }; -fn consputc(c: i32) { +pub fn consputc(c: i32) { if c == BACKSPACE { uartputc('\x08' as i32); uartputc(' ' as i32); diff --git a/src/lib.rs b/src/lib.rs index fa1cc42..4421108 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ fn welcome() { for &byte in data0.iter() { if byte == 0 { break; } - console::consputc(byte as char); + console::consputc(byte as i32); } bio::brelse(b0); From 0baee77e3171d3468d4dddd3ce89cd330a8c836f Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 04:28:43 +0000 Subject: [PATCH 32/69] Updated project level config.toml --- .cargo/config.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0953166 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[build] +target = "targets/i686.json" + +[unstable] +build-std = ["core"] +build-std-features = ["compiler-builtins-mem"] From 3302f511ac33a593eecce959c5445cf1581c1e9d Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 04:47:41 +0000 Subject: [PATCH 33/69] Accumulated older changes --- Cargo.toml | 1 + src/lib.rs | 13 +++++++++++-- src/mp.rs | 12 ++++++++---- src/traps.rs | 6 +----- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2dd5beb..6c2171c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "xv6" version = "0.1.0" edition = "2021" +autobins = false [lib] name = "kernel" diff --git a/src/lib.rs b/src/lib.rs index 4421108..b8c2f0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![no_main] // No main function use core::panic::PanicInfo; +use crate::x86::cli; +use crate::lapic::lapicid; mod param; mod x86; @@ -80,8 +82,15 @@ pub extern "C" fn entryofrust() -> ! { } } +static mut PANICKED: bool = false; + #[panic_handler] fn panic(info: &PanicInfo) -> ! { - println!("Kernel Panic: {:?}", info); - halt() + // Disable interrupts to prevent interrupt handlers from interfering + cli(); + // Print panic message with LAPIC ID to identify which CPU panicked + println!("lapicid {}:\n{:#?}", lapicid(), info); + unsafe { PANICKED = true; } + // Halt the system + loop {} } \ No newline at end of file diff --git a/src/mp.rs b/src/mp.rs index 9684f51..f291c43 100644 --- a/src/mp.rs +++ b/src/mp.rs @@ -4,7 +4,8 @@ use crate::x86::{outb, inb}; use crate::proc::Cpu; use core::cell::OnceCell; -pub static mut IOAPICID: u8 = 0; +// Removed: replaced by MP_ONCE.ioapic_id OnceCell pattern +// pub static mut IOAPICID: u8 = 0; #[derive(Debug, Clone, Copy)] #[repr(C)] @@ -194,7 +195,8 @@ pub fn mpinit() { let conf = unsafe { *(mp.physaddr as *mut MpConf) }; let mut ismp = true; - MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32); + MP_ONCE.lapic_base.set(conf.lapicaddr as *mut u32) + .expect("lapic_base already initialized"); let mut p = (mp.physaddr as usize + mem::size_of::()) as *const u8; let e = (mp.physaddr as usize + conf.length as usize) as *const u8; @@ -218,7 +220,8 @@ pub fn mpinit() { MPIOAPIC => { let ioapic = p as *const MpIoApic; let ioapicid = unsafe { (*ioapic).apicno }; - MP_ONCE.ioapic_id.set(ioapicid); + MP_ONCE.ioapic_id.set(ioapicid) + .expect("ioapic_id already initialized"); // p = unsafe{ p.add(mem::size_of::()) }; p = p.wrapping_add(mem::size_of::()); } @@ -236,7 +239,8 @@ pub fn mpinit() { if !ismp { panic!("Didn't find a suitable machine"); } - MP_ONCE.cpus.set(cpus); + MP_ONCE.cpus.set(cpus) + .expect("cpus array already initialized"); if mp.imcrp != 0 { // Bochs doesn't support IMCR, so this doesn't run on Bochs. diff --git a/src/traps.rs b/src/traps.rs index 4010605..4974746 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -6,13 +6,9 @@ use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt,rcr2}; use crate::lapic; -use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0, SEG_KCODE, STS_IG32, STS_TG32}; use crate::uart::uartintr; -const SEG_KCODE: u16 = 1; -const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate -const STS_TG32: u8 = 0xF; // 32-bit Trap Gate - extern "C" { static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } From 7a888675b5a669bdc61c7188f00f9b0c468ff294 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 05:15:11 +0000 Subject: [PATCH 34/69] Fixed static mutable warnings --- src/console.rs | 43 ++++++++++++++++++++++--------------------- src/ioapic.rs | 4 ++-- src/lib.rs | 2 ++ src/proc.rs | 2 +- src/traps.rs | 5 +++-- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/console.rs b/src/console.rs index 59f186a..61f0aa3 100644 --- a/src/console.rs +++ b/src/console.rs @@ -52,29 +52,30 @@ pub fn consoleintr(getc: fn() -> i32) { break; } - let input = unsafe { &mut INPUT }; - - match c { - x if x == C('U') => { - while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { - input.e -= 1; - consputc(BACKSPACE); + unsafe { + let input = &raw mut INPUT; + match c { + x if x == C('U') => { + while (*input).e != (*input).w && (*input).buf[((*input).e - 1) % INPUT_BUF] != b'\n' { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - x if x == C('H') || x == 0x7f => { - if input.e != input.w { - input.e -= 1; - consputc(BACKSPACE); + x if x == C('H') || x == 0x7f => { + if (*input).e != (*input).w { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - _ => { - if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { - let c = if c == '\r' as i32 { '\n' as i32 } else { c }; - input.buf[input.e % INPUT_BUF] = c as u8; - input.e += 1; - consputc(c); - if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { - input.w = input.e; + _ => { + if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + (*input).buf[(*input).e % INPUT_BUF] = c as u8; + (*input).e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { + (*input).w = (*input).e; + } } } } diff --git a/src/ioapic.rs b/src/ioapic.rs index 15cf837..979cfd0 100644 --- a/src/ioapic.rs +++ b/src/ioapic.rs @@ -1,7 +1,7 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::console::Console; -use core::fmt::Write; +// use crate::console::Console; +// use core::fmt::Write; use crate::println; // I/O APIC default physical address diff --git a/src/lib.rs b/src/lib.rs index b8c2f0a..9727da0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] // No standard library #![no_main] // No main function +#![allow(dead_code)] use core::panic::PanicInfo; use crate::x86::cli; @@ -59,6 +60,7 @@ fn welcome() { bio::brelse(b1); } +// alltraps is an assembly label not a static function pointer in Rust extern "C" { pub fn alltraps(); } diff --git a/src/proc.rs b/src/proc.rs index 279364c..713ad58 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,5 +1,5 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs -use core::ptr; +// use core::ptr; use crate::x86::readeflags; use crate::constants::{FL_IF,NCPU}; use crate::lapic; diff --git a/src/traps.rs b/src/traps.rs index 4974746..4806d32 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,6 +1,7 @@ use modular_bitfield::prelude::*; use core::cell::OnceCell; use core::sync::atomic::{AtomicU32, Ordering}; +use core::ptr::addr_of_mut; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; @@ -86,12 +87,12 @@ pub fn tvinit() { ); } unsafe { - let _ = IDT.set(arr); + let _ = (*addr_of_mut!(IDT)).set(arr); } } pub fn idtinit() { - let idt = unsafe { IDT.get().expect("IDT not initialized") }; + let idt = unsafe { (*addr_of_mut!(IDT)).get().expect("IDT not initialized") }; lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } From a8bbbd7c8f5cda7aa80985b4935dcb161cb3029c Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 16:25:12 +0000 Subject: [PATCH 35/69] p6: Fixed iderw to use interrupt-driven design --- Makefile | 1 - src/ide.rs | 40 +++++++++++++++++++++++++--------------- src/lib.rs | 4 ++-- src/x86.rs | 27 ++++++++++++++------------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 884fb32..4054bdc 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,6 @@ vectors.S: vectors.pl .PRECIOUS: %.o -include *.d -clean: clean: rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img mkfs .gdbinit vectors.S diff --git a/src/ide.rs b/src/ide.rs index 4ae4ee2..180b78e 100644 --- a/src/ide.rs +++ b/src/ide.rs @@ -133,7 +133,9 @@ pub fn ideintr() { } } -// ide.rs +// Sync buf with disk. +// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. +// Else if B_VALID is not set, read buf from disk, set B_VALID. pub fn iderw(idx: usize) { let b = crate::bio::buf_mut(idx); @@ -148,23 +150,31 @@ pub fn iderw(idx: usize) { } } - // PURE POLLING: issue the command directly (no IDEQUEUE). - idestart(idx); - - // Wait for completion. - if idewait(true) < 0 { - panic!("iderw: ide error"); + // Append b to idequeue. + b.qnext = None; + unsafe { + let mut pp: *mut Option = &raw mut IDEQUEUE; + while let Some(next_idx) = *pp { + pp = &raw mut crate::bio::buf_mut(next_idx).qnext; + } + *pp = Some(idx); } - // If it was a read, pull data now. - let flags_now = b.flags.load(Ordering::Acquire); - if (flags_now & B_DIRTY) == 0 { - unsafe { - crate::x86::insl(0x1F0, b.data.as_mut_ptr() as *mut u32, BSIZE / 4); + // Start disk if necessary. + unsafe { + if IDEQUEUE == Some(idx) { + idestart(idx); } } - // If it was a write, data was already pushed in idestart() via outsl(). - b.flags.fetch_or(B_VALID, Ordering::AcqRel); - b.flags.fetch_and(!B_DIRTY, Ordering::AcqRel); + // Wait for request to finish. + // The interrupt handler will set B_VALID when done. + loop { + let flags = b.flags.load(Ordering::Acquire); + if (flags & (B_VALID | B_DIRTY)) == B_VALID { + break; + } + // Force compiler to re-read b->flags which is modified by ideintr() + x86::noop(); + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9727da0..21209db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,8 @@ macro_rules! println { fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { - x86::outw(0x604, 0x2000); - x86::outw(0xB004, 0x2000); + x86::outw(0x602, 0x2000); // QEMU isa-debug-exit device + x86::outw(0xB002, 0x2000); // VirtualBox shutdown port } } diff --git a/src/x86.rs b/src/x86.rs index fb341ef..ba0db57 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -129,22 +129,23 @@ pub unsafe fn insl(port: u16, addr: *mut u32, cnt: usize) { } - +// Output string of dwords to port using rep outsd instruction. +// Matches the C implementation: outsl(port, addr, cnt) +// Note: ESI must be saved/restored as LLVM restricts its use in 32-bit mode pub unsafe fn outsl(port: u16, addr: *const u32, cnt: usize) { - let mut p = addr; - for _ in 0..cnt { - let val = core::ptr::read(p); - asm!( - "out dx, eax", - in("dx") port, - in("eax") val, - options(nomem, nostack, preserves_flags), - ); - p = p.add(1); - } + let addr_val = addr as u32; + core::arch::asm!( + "push esi", + "mov esi, {addr}", + "cld", + "rep outsd", + "pop esi", + addr = in(reg) addr_val, + in("dx") port, + inout("ecx") cnt => _, + ); } - /// Halts the CPU until the next interrupt occurs. /// The `hlt` instruction: /// 1. Stops instruction execution and places the processor in a HALT state From e51630d8ea1220e265f891d50e1fbb059092ddcd Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 16:39:04 +0000 Subject: [PATCH 36/69] Updated port exit status --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 21209db..b714a15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,8 @@ macro_rules! println { fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { - x86::outw(0x602, 0x2000); // QEMU isa-debug-exit device - x86::outw(0xB002, 0x2000); // VirtualBox shutdown port + x86::outw(0x604, 0x2000); // QEMU isa-debug-exit device + x86::outw(0xB004, 0x2000); // VirtualBox shutdown port } } From fff2425f85fabd24bfa161bfa7e8f6324f2de47f Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 04:52:12 +0000 Subject: [PATCH 37/69] Added constants and fixed welcome() bug --- .gdbinit.tmpl | 1 + Makefile | 9 +---- src/buf.rs | 6 ++- src/constants.rs | 45 +++++++++++++++++++++- src/fs.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ide.rs | 3 +- src/lib.rs | 16 +++----- src/mkfs.rs | 61 +++++++++++++++++++++-------- 8 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 src/fs.rs diff --git a/.gdbinit.tmpl b/.gdbinit.tmpl index f71681a..1dfd173 100644 --- a/.gdbinit.tmpl +++ b/.gdbinit.tmpl @@ -25,3 +25,4 @@ target remote localhost:1234 echo + symbol-file kernel\n symbol-file kernel +layout split \ No newline at end of file diff --git a/Makefile b/Makefile index 4054bdc..c8eb197 100644 --- a/Makefile +++ b/Makefile @@ -54,12 +54,7 @@ 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 *.txt - ./mkfs fs.img *.txt - +# Build mkfs utility and create filesystem image mkfs: src/mkfs.rs rustc -W warnings -o mkfs src/mkfs.rs @@ -127,6 +122,6 @@ qemu: xv6.img fs.img .gdbinit: .gdbinit.tmpl sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ -qemu-gdb: xv6.img fs .gdbinit +qemu-gdb: xv6.img .gdbinit fs.img @echo "*** Now run 'gdb'." 1>&2 $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/buf.rs b/src/buf.rs index 2c4cfb2..95a5faa 100644 --- a/src/buf.rs +++ b/src/buf.rs @@ -1,8 +1,10 @@ use core::sync::atomic::AtomicU32; -pub const BSIZE: usize = 512; // block size -pub const NBUF: usize = 30; // same as xv6 default; can tune later +// Re-export filesystem constants from constants.rs for convenience +// (other modules currently import BSIZE and NBUF from buf) +pub use crate::constants::{BSIZE, NBUF}; +// Buffer flags 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 diff --git a/src/constants.rs b/src/constants.rs index b36418c..40ea01c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -113,4 +113,47 @@ pub const X1: u32 = 0x0000000B; // divide counts by 1 pub const PERIODIC: u32 = 0x00020000; // Periodic // Error handling -pub const MASKED: u32 = 0x00010000; // Interrupt masked \ No newline at end of file +pub const MASKED: u32 = 0x00010000; // Interrupt masked + +// ------------------------------------------------------ FILE SYSTEM (fs.h) ---------------------------------------------- +// On-disk file system format constants +// Both the kernel and user programs (mkfs) use these definitions + +pub const ROOTINO: u32 = 1; // root i-number +pub const BSIZE: usize = 512; // block size + +// File structure +pub const NDIRECT: usize = 12; // number of direct block pointers +pub const NINDIRECT: usize = BSIZE / core::mem::size_of::(); // number of indirect block pointers (BSIZE / sizeof(uint)) +pub const MAXFILE: usize = NDIRECT + NINDIRECT; // max file size in blocks + +// Directory entry +pub const DIRSIZ: usize = 14; // directory name length + +// Inode calculations +// Note: In C these are macros. IPB = (BSIZE / sizeof(struct dinode)) +// For Rust: sizeof(dinode) = 2+2+2+2+4+(13*4) = 64 bytes +// So IPB = 512/64 = 8 inodes per block +pub const DINODE_SIZE: usize = 64; // sizeof(struct dinode) +pub const IPB: usize = BSIZE / DINODE_SIZE; // inodes per block + +// Bitmap calculations +pub const BPB: usize = BSIZE * 8; // bitmap bits per block + +// ------------------------------------------------------ SYSTEM PARAMETERS (param.h) ---------------------------------------------- +// System-wide parameters + +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const ROOTDEV: u32 = 1; // device number of file system root disk +pub const LOGSIZE: u32 = 30; // max data blocks in on-disk log (C version: 0, extended version: 30) +pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache +pub const FSSIZE: u32 = 10000; // size of file system in blocks (C version: 1000, extended: 10000) +pub const NINODES: u32 = 200; // number of inodes in file system + +// ------------------------------------------------------ FILE TYPES (stat.h) ---------------------------------------------- +// File type constants for inode.type field + +pub const T_DIR: u16 = 1; // Directory +pub const T_FILE: u16 = 2; // File +pub const T_DEV: u16 = 3; // Device \ No newline at end of file diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..730757b --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,99 @@ +// On-disk file system format. +// Both the kernel and user programs use this header file (in C version). +// This is the Rust equivalent of fs.h + +use crate::constants::*; + +// Disk layout: +// [ boot block | super block | log | inode blocks | +// free bit map | data blocks] +// +// mkfs computes the super block and builds an initial file system. The +// super block describes the disk layout: + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Superblock { + pub size: u32, // Size of file system image (blocks) + pub nblocks: u32, // Number of data blocks + pub ninodes: u32, // Number of inodes + pub nlog: u32, // Number of log blocks + pub logstart: u32, // Block number of first log block + pub inodestart: u32, // Block number of first inode block + pub bmapstart: u32, // Block number of first free map block +} + +// On-disk inode structure +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Dinode { + pub typ: u16, // File type (use 'typ' as 'type' is a Rust keyword) + pub major: u16, // Major device number (T_DEV only) + pub minor: u16, // Minor device number (T_DEV only) + pub nlink: u16, // Number of links to inode in file system + pub size: u32, // Size of file (bytes) + pub addrs: [u32; NDIRECT + 1], // Data block addresses +} + +// Directory entry structure +// A directory is a file containing a sequence of dirent structures. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Dirent { + pub inum: u16, // Inode number + pub name: [u8; DIRSIZ], // Directory name (max DIRSIZ chars) +} + +// Helper functions that match C macros from fs.h + +/// Inodes per block: IPB = (BSIZE / sizeof(struct dinode)) +pub const fn inodes_per_block() -> usize { + BSIZE / core::mem::size_of::() +} + +/// Block containing inode i: IBLOCK(i, sb) = ((i) / IPB + sb.inodestart) +pub fn iblock(inum: u32, sb: &Superblock) -> u32 { + (inum / inodes_per_block() as u32) + u32::from_le(sb.inodestart) +} + +/// Block of free map containing bit for block b: BBLOCK(b, sb) = (b/BPB + sb.bmapstart) +pub fn bblock(blockno: u32, sb: &Superblock) -> u32 { + (blockno / BPB as u32) + u32::from_le(sb.bmapstart) +} + +// Default implementations for convenience +impl Default for Superblock { + fn default() -> Self { + Self { + size: 0, + nblocks: 0, + ninodes: 0, + nlog: 0, + logstart: 0, + inodestart: 0, + bmapstart: 0, + } + } +} + +impl Default for Dinode { + fn default() -> Self { + Self { + typ: 0, + major: 0, + minor: 0, + nlink: 0, + size: 0, + addrs: [0; NDIRECT + 1], + } + } +} + +impl Default for Dirent { + fn default() -> Self { + Self { + inum: 0, + name: [0; DIRSIZ], + } + } +} diff --git a/src/ide.rs b/src/ide.rs index 180b78e..99619f2 100644 --- a/src/ide.rs +++ b/src/ide.rs @@ -1,5 +1,6 @@ use core::sync::atomic::Ordering; -use crate::buf::{BSIZE, B_DIRTY, B_VALID}; +use crate::constants::BSIZE; +use crate::buf::{B_DIRTY, B_VALID}; use crate::x86; const SECTOR_SIZE: usize = 512; diff --git a/src/lib.rs b/src/lib.rs index b714a15..4bb252f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,8 @@ mod picirq; mod mp; mod proc; mod traps; -mod constants; +mod constants; // Internal use only - no external crates +mod fs; // Internal use only - filesystem structures mod buf; mod bio; mod ide; @@ -41,16 +42,9 @@ fn halt() -> ! { } fn welcome() { - let b0 = bio::bread(1, 0); - let data0 = bio::buf_mut(b0).data; - - for &byte in data0.iter() { - if byte == 0 { break; } - console::consputc(byte as i32); - } - bio::brelse(b0); - - let b1 = bio::bread(1, 1); + // Read boot counter from block 0 of device 1 (fs.img) + // Changed from block 1 to block 0 to match C version (p7-mkfs) + let b1 = bio::bread(1, 0); let count = bio::buf_mut(b1).data[0]; println!("\nAfter preparing fs.img, we have rebooted {} times\n", count); diff --git a/src/mkfs.rs b/src/mkfs.rs index 9bfb77b..53fb795 100644 --- a/src/mkfs.rs +++ b/src/mkfs.rs @@ -5,22 +5,51 @@ use std::mem::size_of; use std::path::Path; -// constants to be included in constants.rs under "FILE SYSTEM" section, -// declared here for now to avoid merge conflicts - -const BSIZE: usize = 512; -const FSSIZE: u32 = 10_000; -const NINODES: u32 = 200; -const LOGSIZE: u32 = 30; - -const ROOTINO: u32 = 1; -const NDIRECT: usize = 12; -const NINDIRECT: usize = BSIZE / size_of::(); -const MAXFILE: usize = NDIRECT + NINDIRECT; -const DIRSIZ: usize = 14; - -const T_DIR: u16 = 1; -const T_FILE: u16 = 2; +// ============================================================================ +// FILE SYSTEM CONSTANTS AND STRUCTURES +// ============================================================================ +// NOTE: These definitions are duplicated from the kernel codebase. +// +// WHY STANDALONE? +// In the C version, mkfs.c is compiled standalone with `gcc -o mkfs mkfs.c` +// and shares definitions via header files (fs.h, param.h, stat.h). +// +// Rust cannot use header files, so we duplicate the definitions here. +// This matches C's semantic model: mkfs is a host-side utility that runs +// on Linux/Mac (with std) to create fs.img BEFORE the kernel boots. +// +// The kernel (src/*) runs bare-metal x86 (no_std) and cannot share compiled +// code with host programs. +// +// MAINTENANCE: Keep synchronized with: +// - src/constants.rs (constants) +// - src/fs.rs (structs) - once created +// ============================================================================ + +// From fs.h (On-disk file system format) +const ROOTINO: u32 = 1; // root i-number +const BSIZE: usize = 512; // block size +const NDIRECT: usize = 12; // number of direct block pointers +const NINDIRECT: usize = BSIZE / size_of::(); // indirect pointers +const MAXFILE: usize = NDIRECT + NINDIRECT; // max file size in blocks +const DIRSIZ: usize = 14; // directory name length + +// From param.h (System parameters) +const FSSIZE: u32 = 10_000; // size of file system in blocks +const NINODES: u32 = 200; // number of inodes +const LOGSIZE: u32 = 30; // max data blocks in on-disk log + +// From stat.h (File types) +const T_DIR: u16 = 1; // Directory +const T_FILE: u16 = 2; // File + +// ============================================================================ +// FILESYSTEM STRUCTURES (Duplicated from src/fs.rs) +// ============================================================================ +// In C: These are defined in fs.h and included by both kernel and mkfs.c +// In Rust: Canonical definitions are in src/fs.rs (for kernel) +// Duplicated here because mkfs is a standalone host binary +// ============================================================================ #[repr(C)] #[derive(Clone, Copy, Default)] From 30e13e2d42684b7c81bc3d9d65b4fd60dcedec4c Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 06:21:52 +0000 Subject: [PATCH 38/69] Updated bootblock.ld --- Makefile | 9 ++++----- linkers/bootblock.ld | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 linkers/bootblock.ld diff --git a/Makefile b/Makefile index 56e0958..4084a68 100644 --- a/Makefile +++ b/Makefile @@ -69,13 +69,12 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -bootblock: bootasm.S bootmain.c +bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(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 + $(LD) $(LDFLAGS) -T linkers/bootblock.ld -o bootblock.o bootasm.o bootmain.o + $(OBJDUMP) -S bootblock.o > bootblock.asm + $(OBJCOPY) -S -O binary bootblock.o bootblock kernel.a: $(RS) 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 diff --git a/linkers/bootblock.ld b/linkers/bootblock.ld new file mode 100644 index 0000000..6ac5c34 --- /dev/null +++ b/linkers/bootblock.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) + +SECTIONS +{ + . = 0x7c00; + + .text : { + *(.text .text.*) + } + + .rodata : { + *(.rodata .rodata.*) + } + + .data : { + *(.data .data.*) + } + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } + + . = 0x7c00 + 510; + .signature : { + SHORT(0xaa55) + } +} From f8ccfd5aabd836866819bc27306d083e28333e80 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 06:28:43 +0000 Subject: [PATCH 39/69] Updated bootblock.ld --- Makefile | 9 ++++----- bootasm.S | 4 +++- linkers/bootblock.ld | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 linkers/bootblock.ld diff --git a/Makefile b/Makefile index 6e9abeb..23f31c8 100644 --- a/Makefile +++ b/Makefile @@ -61,13 +61,12 @@ fs.img: welcome.txt dd if=/dev/zero of=fs.img count=2 dd if=welcome.txt of=fs.img conv=notrunc -bootblock: bootasm.S bootmain.c +bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(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 + $(LD) $(LDFLAGS) -T linkers/bootblock.ld -o bootblock.o bootasm.o bootmain.o + $(OBJDUMP) -S bootblock.o > bootblock.asm + $(OBJCOPY) -S -O binary bootblock.o bootblock kernel.a: $(RS) cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ diff --git a/bootasm.S b/bootasm.S index 81c0d52..26f81d0 100644 --- a/bootasm.S +++ b/bootasm.S @@ -83,4 +83,6 @@ gdt: gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 - .long gdt # address gdt \ No newline at end of file + .long gdt # address gdt + +.section .note.GNU-stack,"",@progbits \ No newline at end of file diff --git a/linkers/bootblock.ld b/linkers/bootblock.ld new file mode 100644 index 0000000..6ac5c34 --- /dev/null +++ b/linkers/bootblock.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) + +SECTIONS +{ + . = 0x7c00; + + .text : { + *(.text .text.*) + } + + .rodata : { + *(.rodata .rodata.*) + } + + .data : { + *(.data .data.*) + } + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } + + . = 0x7c00 + 510; + .signature : { + SHORT(0xaa55) + } +} From 92f17690e3b660231f45de418b03508f97f3178b Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 06:31:29 +0000 Subject: [PATCH 40/69] Updated bootblock.ld --- Makefile | 9 ++++----- bootasm.S | 4 +++- linkers/bootblock.ld | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 linkers/bootblock.ld diff --git a/Makefile b/Makefile index c8eb197..36c7cb5 100644 --- a/Makefile +++ b/Makefile @@ -61,13 +61,12 @@ mkfs: src/mkfs.rs fs.img: mkfs *.txt ./mkfs fs.img *.txt -bootblock: bootasm.S bootmain.c +bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(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 + $(LD) $(LDFLAGS) -T linkers/bootblock.ld -o bootblock.o bootasm.o bootmain.o + $(OBJDUMP) -S bootblock.o > bootblock.asm + $(OBJCOPY) -S -O binary bootblock.o bootblock kernel.a: $(RS) cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ diff --git a/bootasm.S b/bootasm.S index 81c0d52..26f81d0 100644 --- a/bootasm.S +++ b/bootasm.S @@ -83,4 +83,6 @@ gdt: gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 - .long gdt # address gdt \ No newline at end of file + .long gdt # address gdt + +.section .note.GNU-stack,"",@progbits \ No newline at end of file diff --git a/linkers/bootblock.ld b/linkers/bootblock.ld new file mode 100644 index 0000000..6ac5c34 --- /dev/null +++ b/linkers/bootblock.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) + +SECTIONS +{ + . = 0x7c00; + + .text : { + *(.text .text.*) + } + + .rodata : { + *(.rodata .rodata.*) + } + + .data : { + *(.data .data.*) + } + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } + + . = 0x7c00 + 510; + .signature : { + SHORT(0xaa55) + } +} From e3e532229b78868a46506210043e3ff9aa3cb650 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 06:58:53 +0000 Subject: [PATCH 41/69] Update bootblock.ld --- Makefile | 9 ++++----- bootasm.S | 4 +++- linkers/bootblock.ld | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 linkers/bootblock.ld diff --git a/Makefile b/Makefile index 0df672e..7ccf0c2 100644 --- a/Makefile +++ b/Makefile @@ -75,13 +75,12 @@ mkfs: ../col331/mkfs.c ../col331/fs.h ../col331/types.h ../col331/stat.h ../col3 fs.img: mkfs welcome.txt ./mkfs fs.img welcome.txt -bootblock: bootasm.S bootmain.c +bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(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 - perl sign.pl bootblock + $(LD) $(LDFLAGS) -T linkers/bootblock.ld -o bootblock.o bootasm.o bootmain.o + $(OBJDUMP) -S bootblock.o > bootblock.asm + $(OBJCOPY) -S -O binary bootblock.o bootblock kernel.a: $(RS) 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 diff --git a/bootasm.S b/bootasm.S index 81c0d52..26f81d0 100644 --- a/bootasm.S +++ b/bootasm.S @@ -83,4 +83,6 @@ gdt: gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 - .long gdt # address gdt \ No newline at end of file + .long gdt # address gdt + +.section .note.GNU-stack,"",@progbits \ No newline at end of file diff --git a/linkers/bootblock.ld b/linkers/bootblock.ld new file mode 100644 index 0000000..6ac5c34 --- /dev/null +++ b/linkers/bootblock.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) + +SECTIONS +{ + . = 0x7c00; + + .text : { + *(.text .text.*) + } + + .rodata : { + *(.rodata .rodata.*) + } + + .data : { + *(.data .data.*) + } + + /DISCARD/ : { + *(.eh_frame .note.GNU-stack .comment) + } + + . = 0x7c00 + 510; + .signature : { + SHORT(0xaa55) + } +} From 375bdf6860e863666b4ff96269d3abf023ff4fcd Mon Sep 17 00:00:00 2001 From: Arinjay Singhal <137890548+ArinjaySinghal19@users.noreply.github.com> Date: Mon, 16 Feb 2026 17:19:34 +0530 Subject: [PATCH 42/69] p5: add UART interrupt and console input support --- Makefile | 65 +++++++------------------ src/console.rs | 71 ++++++++++++++++++++++++--- src/lapic.rs | 2 +- src/lib.rs | 9 +++- src/traps.rs | 128 ++++++++++++++++++++++++++++++------------------- src/uart.rs | 31 ++++++++++-- 6 files changed, 198 insertions(+), 108 deletions(-) diff --git a/Makefile b/Makefile index 4084a68..883e6e8 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,6 @@ OBJS = entry.o vectors.o trapasm.o RS = src/*.rs -# Cross-compiling (e.g., on Mac OS X) -#TOOLPREFIX = i386-jos-elf -#TOOLPREFIX = i386-elf- - -# Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = - -# 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; \ then echo 'i386-jos-elf-'; \ @@ -16,18 +8,9 @@ TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/d then echo ''; \ else echo "***" 1>&2; \ echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) + exit 1; fi) endif -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU ifndef QEMU QEMU = $(shell if which qemu > /dev/null; \ then echo qemu; exit; \ @@ -35,14 +18,7 @@ QEMU = $(shell if which qemu > /dev/null; \ then echo qemu-system-i386; exit; \ elif which qemu-system-x86_64 > /dev/null; \ then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) + else echo "*** Error: Couldn't find QEMU." 1>&2; exit 1; fi) endif CC = $(TOOLPREFIX)gcc @@ -50,13 +26,13 @@ AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump + CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) + ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -# FreeBSD ld wants ``elf_i386_fbsd'' LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) -# Disable PIE when possible (for Ubuntu 16.10 toolchain) ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) CFLAGS += -fno-pie -no-pie endif @@ -77,45 +53,40 @@ bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(OBJCOPY) -S -O binary bootblock.o bootblock kernel.a: $(RS) - 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 + cargo +nightly 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 vectors.S: vectors.pl ./vectors.pl > vectors.S -# $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o kernel.a -b binary -# ld -m elf_i386 -T kernel.ld -o kernel entry.o kernel.a -b binary -# Prevent deletion of intermediate files, e.g. cat.o, after first build, so -# that disk image changes after first build are persistent until clean. More -# details: -# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html .PRECIOUS: %.o - -include *.d -clean: +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 + rm -rf target -# run in emulators -# try to generate a unique GDB port GDBPORT = $(shell expr `id -u` % 5000 + 25000) -# QEMU's gdb stub command line changed in 0.11 QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ then echo "-gdb tcp::$(GDBPORT)"; \ else echo "-s -p $(GDBPORT)"; fi) + ifndef CPUS - CPUS := 1 +CPUS := 1 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 -smp $(CPUS) -m 512 qemu: xv6.img $(QEMU) -nographic $(QEMUOPTS) @@ -125,4 +96,4 @@ qemu: xv6.img qemu-gdb: xv6.img .gdbinit @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) \ No newline at end of file + $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/console.rs b/src/console.rs index 0f62712..25bde2f 100644 --- a/src/console.rs +++ b/src/console.rs @@ -5,20 +5,79 @@ pub struct Console {} impl Write for Console { fn write_str(&mut self, s: &str) -> Result { for c in s.chars() { - consputc(c); + consputc(c as i32); } Ok(()) } } -const BACKSPACE: char = '\x08'; +const BACKSPACE: i32 = 0x100; +const INPUT_BUF: usize = 128; +const CTRL_D: i32 = C('D'); -fn consputc(c: char) { +#[allow(non_snake_case)] +const fn C(c: char) -> i32 { + (c as i32) - ('@' as i32) +} + +#[derive(Clone, Copy)] +struct Input { + buf: [u8; INPUT_BUF], + r: usize, + w: usize, + e: usize, +} + +static mut INPUT: Input = Input { + buf: [0; INPUT_BUF], + r: 0, + w: 0, + e: 0, +}; + +fn consputc(c: i32) { if c == BACKSPACE { - uartputc(BACKSPACE); - uartputc(' '); - uartputc(BACKSPACE); + uartputc('\x08' as i32); + uartputc(' ' as i32); + uartputc('\x08' as i32); } else { uartputc(c); } +} + +pub fn consoleintr(getc: fn() -> i32) { + loop { + let c = getc(); + if c < 0 { + break; + } + + let input = unsafe { &mut INPUT }; + + match c { + x if x == C('U') => { + while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { + input.e -= 1; + consputc(BACKSPACE); + } + } + x if x == C('H') || x == 0x7f => { + if input.e != input.w { + input.e -= 1; + consputc(BACKSPACE); + } + } + _ => { + if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + input.buf[input.e % INPUT_BUF] = c as u8; + input.e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { + input.w = input.e; + } + } + } + } + } } \ No newline at end of file diff --git a/src/lapic.rs b/src/lapic.rs index 3a1aa17..4f6306d 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -70,7 +70,7 @@ pub fn lapicinit() { // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 1000000000); + lapicw(TICR, 10000000); // Disable logical interrupt lines. diff --git a/src/lib.rs b/src/lib.rs index 7808508..51de53c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,8 +45,8 @@ fn panic(info: &PanicInfo) -> ! { fn halt() -> ! { println!("Bye COL{}\n\0", 331); loop { - x86::outw(0x604, 0x2000); - x86::outw(0xB004, 0x2000); + x86::outw(0x602, 0x2000); + x86::outw(0xB002, 0x2000); } } @@ -69,3 +69,8 @@ pub extern "C" fn entryofrust() -> ! { } } +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + println!("Kernel Panic: {:?}", info); + halt() +} \ No newline at end of file diff --git a/src/traps.rs b/src/traps.rs index aa1c1a3..a640548 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,19 +1,25 @@ use modular_bitfield::prelude::*; -use core::cell::{OnceCell, RefCell}; +use core::cell::OnceCell; +use core::sync::atomic::{AtomicU32, Ordering}; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt, rcr2, TrapFrame}; use crate::lapic; -use crate::constants::{SEG_KCODE, STS_IG32, STS_TG32, T_IRQ0, IRQ_TIMER, IRQ_ERROR, IRQ_SPURIOUS}; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::uart::uartintr; + +const SEG_KCODE: u16 = 1; +const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate +const STS_TG32: u8 = 0xF; // 32-bit Trap Gate extern "C" { - static vectors: [usize; 256]; // remove assembly. + static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } #[bitfield] #[repr(C, packed)] -#[derive(Clone, Copy, Default)] // debug can be removed ? do we need to ? +#[derive(Clone, Copy, Default)] pub struct GateDesc { off_15_0: B16, // low 16 bits of offset in segment cs: B16, // code segment selector @@ -32,7 +38,7 @@ impl GateDesc { self.set_args(0); self.set_rsv1(0); let typ = if is_trap { STS_TG32 } else { STS_IG32 }; - self.set_r_type(typ); // for an interrupt gate, for example. + self.set_r_type(typ); self.set_s(0); self.set_dpl(dpl); self.set_p(1); @@ -41,35 +47,56 @@ impl GateDesc { } +static mut IDT: OnceCell<[GateDesc; 256]> = OnceCell::new(); +pub static TICKS: AtomicU32 = AtomicU32::new(0); + + #[repr(C)] -pub struct IDTOnce { - pub idt: OnceCell<[GateDesc; 256]>, -} -unsafe impl Sync for IDTOnce {} -pub static IDT: IDTOnce = IDTOnce { idt: OnceCell::new() }; - -pub struct TickCounter { - ticks: RefCell +pub struct TrapFrame { + // registers as pushed by pusha + pub edi: u32, + pub esi: u32, + pub ebp: u32, + pub oesp: u32, // useless & ignored + pub ebx: u32, + pub edx: u32, + pub ecx: u32, + pub eax: u32, + + pub trapno: u32, + + // below here defined by x86 hardware + pub err: u32, + pub eip: u32, + pub cs: u16, + pub padding5: u16, + pub eflags: u32, + + // below here only when crossing rings, such as from user to kernel + pub esp: u32, + pub ss: u16, + pub padding6: u16, } -unsafe impl Sync for TickCounter {} -static TICKS: TickCounter = TickCounter { ticks: RefCell::new(0) }; pub fn tvinit() { let mut arr = [GateDesc::default(); 256]; for i in 0..256 { arr[i].set_gate( - false, // Use an interrupt gate. - SEG_KCODE << 3, // Code segment selector (shifted as in the C code). - unsafe { vectors[i] }, // Offset from the external vector table. - 0 // Descriptor privilege level. + false, + SEG_KCODE << 3, + unsafe { vectors[i] }, + 0 ); } - IDT.idt.set(arr); + unsafe { + let _ = IDT.set(arr); + } } pub fn idtinit() { - lidt(IDT.idt.get().unwrap() , core::mem::size_of::<[GateDesc; 256]>() as usize); + let idt = unsafe { IDT.get().expect("IDT not initialized") }; + lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } @@ -82,34 +109,37 @@ pub extern "C" fn trap(orig_tf: *mut TrapFrame) { let tf = unsafe { &mut *orig_tf }; - const TIMER: u32 = T_IRQ0 + IRQ_TIMER; - const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; - const SEVEN: u32 = T_IRQ0 + 7; - + const TIMER: u32 = T_IRQ0 + IRQ_TIMER; + const SPURIOUS: u32 = T_IRQ0 + IRQ_SPURIOUS; + const SEVEN: u32 = T_IRQ0 + 7; + match tf.trapno { - TIMER => { - *TICKS.ticks.borrow_mut() += 1; - println!("Tick {}!", TICKS.ticks.borrow()); - lapic::lapiceoi(); - } - SEVEN | SPURIOUS => { - println!( - "cpu{}: spurious interrupt at {}:{}\n", - cpuid() , - tf.cs, - tf.eip - ); + TIMER => { + TICKS.fetch_add(1, Ordering::Relaxed); + lapic::lapiceoi(); + } + x if x == T_IRQ0 + IRQ_COM1 => { + uartintr(); lapiceoi(); - } - _ => { - println!( - "unexpected trap {} from cpu {} eip {} (cr2=0x{:x})\n", - tf.trapno, - cpuid(), - tf.eip, - rcr2() - ); - panic!("trap happened"); - } - } + } + SEVEN | SPURIOUS => { + println!( + "cpu{}: spurious interrupt at {:x}:{:x}\n", + cpuid(), + tf.cs, + tf.eip + ); + lapiceoi(); + } + _ => { + println!( + "unexpected trap {} from cpu {} eip {:x} (cr2=0x{:x})\n", + tf.trapno, + cpuid(), + tf.eip, + rcr2() + ); + panic!("trap"); + } + } } \ No newline at end of file diff --git a/src/uart.rs b/src/uart.rs index 9888088..aaf3137 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,7 +1,13 @@ -use crate::x86::{outb, inb}; +use crate::console::consoleintr; +use crate::x86::{inb, outb}; use crate::ioapic::ioapic_enable; +use core::sync::atomic::{AtomicBool, Ordering}; + const COM1: u16 = 0x3F8; // COM1 port address pub const IRQ_COM1: u32 = 4; + +static UART_READY: AtomicBool = AtomicBool::new(false); + pub fn uartinit() { // Turn off the FIFO. outb(COM1 + 2, 0); @@ -19,6 +25,7 @@ pub fn uartinit() { if inb(COM1 + 5) == 0xFF { return; } + UART_READY.store(true, Ordering::SeqCst); // Acknowledge pre-existing interrupt conditions; // enable interrupts. @@ -28,15 +35,33 @@ pub fn uartinit() { // Announce that the UART is active. for p in "xv6...\n".chars() { - uartputc(p); + uartputc(p as i32); } } -pub fn uartputc(c: char) { +pub fn uartputc(c: i32) { + if !UART_READY.load(Ordering::SeqCst) { + return; + } + for _ in 0..128 { if inb(COM1 + 5) & 0x20 != 0 { break; } } outb(COM1 + 0, c as u8); +} + +fn uartgetc() -> i32 { + if !UART_READY.load(Ordering::SeqCst) { + return -1; + } + if (inb(COM1 + 5) & 0x01) == 0 { + return -1; + } + inb(COM1) as i32 +} + +pub fn uartintr() { + consoleintr(uartgetc); } \ No newline at end of file From f84251805bdf97ac1ae4f7fb462ab379ba7360e6 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 04:47:41 +0000 Subject: [PATCH 43/69] Accumulated older changes --- src/lib.rs | 11 +++++++++-- src/mp.rs | 1 + src/traps.rs | 6 +----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 51de53c..3800c4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,8 +69,15 @@ pub extern "C" fn entryofrust() -> ! { } } +static mut PANICKED: bool = false; + #[panic_handler] fn panic(info: &PanicInfo) -> ! { - println!("Kernel Panic: {:?}", info); - halt() + // Disable interrupts to prevent interrupt handlers from interfering + cli(); + // Print panic message with LAPIC ID to identify which CPU panicked + println!("lapicid {}:\n{:#?}", lapicid(), info); + unsafe { PANICKED = true; } + // Halt the system + loop {} } \ No newline at end of file diff --git a/src/mp.rs b/src/mp.rs index 3eecf6a..1b5117e 100644 --- a/src/mp.rs +++ b/src/mp.rs @@ -222,6 +222,7 @@ pub fn mpinit() { let ioapicid = unsafe { (*ioapic).apicno }; MP_ONCE.ioapic_id.set(ioapicid) .expect("ioapic_id already initialized"); + // p = unsafe{ p.add(mem::size_of::()) }; p = p.wrapping_add(mem::size_of::()); } MPBUS | MPIOINTR | MPLINTR => { diff --git a/src/traps.rs b/src/traps.rs index a640548..c1a0f1e 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -6,13 +6,9 @@ use crate::println; use crate::lapic::lapiceoi; use crate::x86::{lidt, rcr2, TrapFrame}; use crate::lapic; -use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::constants::{IRQ_COM1, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0, SEG_KCODE, STS_IG32, STS_TG32}; use crate::uart::uartintr; -const SEG_KCODE: u16 = 1; -const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate -const STS_TG32: u8 = 0xF; // 32-bit Trap Gate - extern "C" { static vectors: [usize; 256]; // in vectors.S: array of 256 entry pointers } From 8ad812bd5c673394d2ecd3107ad9c48c96f31e07 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Mon, 2 Mar 2026 05:15:11 +0000 Subject: [PATCH 44/69] Fixed static mutable warnings --- src/console.rs | 43 ++++++++++++++++++++++--------------------- src/ioapic.rs | 4 ++-- src/lib.rs | 4 +++- src/proc.rs | 2 +- src/traps.rs | 5 +++-- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/console.rs b/src/console.rs index 25bde2f..507152b 100644 --- a/src/console.rs +++ b/src/console.rs @@ -52,29 +52,30 @@ pub fn consoleintr(getc: fn() -> i32) { break; } - let input = unsafe { &mut INPUT }; - - match c { - x if x == C('U') => { - while input.e != input.w && input.buf[(input.e - 1) % INPUT_BUF] != b'\n' { - input.e -= 1; - consputc(BACKSPACE); + unsafe { + let input = &raw mut INPUT; + match c { + x if x == C('U') => { + while (*input).e != (*input).w && (*input).buf[((*input).e - 1) % INPUT_BUF] != b'\n' { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - x if x == C('H') || x == 0x7f => { - if input.e != input.w { - input.e -= 1; - consputc(BACKSPACE); + x if x == C('H') || x == 0x7f => { + if (*input).e != (*input).w { + (*input).e -= 1; + consputc(BACKSPACE); + } } - } - _ => { - if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { - let c = if c == '\r' as i32 { '\n' as i32 } else { c }; - input.buf[input.e % INPUT_BUF] = c as u8; - input.e += 1; - consputc(c); - if c == '\n' as i32 || c == CTRL_D || input.e == input.r + INPUT_BUF { - input.w = input.e; + _ => { + if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { + let c = if c == '\r' as i32 { '\n' as i32 } else { c }; + (*input).buf[(*input).e % INPUT_BUF] = c as u8; + (*input).e += 1; + consputc(c); + if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { + (*input).w = (*input).e; + } } } } diff --git a/src/ioapic.rs b/src/ioapic.rs index 1196087..eaf9d70 100644 --- a/src/ioapic.rs +++ b/src/ioapic.rs @@ -1,7 +1,7 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::console::Console; -use core::fmt::Write; +// use crate::console::Console; +// use core::fmt::Write; use crate::println; // I/O APIC default physical address diff --git a/src/lib.rs b/src/lib.rs index 3800c4b..2e70ea4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] // No standard library #![no_main] // No main function +#![allow(dead_code)] use core::panic::PanicInfo; use crate::x86::cli; @@ -50,8 +51,9 @@ fn halt() -> ! { } } +// alltraps is an assembly label not a static function pointer extern "C" { - pub static alltraps: fn(); + fn alltraps(); } #[no_mangle] diff --git a/src/proc.rs b/src/proc.rs index 279364c..713ad58 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,5 +1,5 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs -use core::ptr; +// use core::ptr; use crate::x86::readeflags; use crate::constants::{FL_IF,NCPU}; use crate::lapic; diff --git a/src/traps.rs b/src/traps.rs index c1a0f1e..274e301 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -1,6 +1,7 @@ use modular_bitfield::prelude::*; use core::cell::OnceCell; use core::sync::atomic::{AtomicU32, Ordering}; +use core::ptr::addr_of_mut; use crate::proc::cpuid; use crate::println; use crate::lapic::lapiceoi; @@ -86,12 +87,12 @@ pub fn tvinit() { ); } unsafe { - let _ = IDT.set(arr); + let _ = (*addr_of_mut!(IDT)).set(arr); } } pub fn idtinit() { - let idt = unsafe { IDT.get().expect("IDT not initialized") }; + let idt = unsafe { (*addr_of_mut!(IDT)).get().expect("IDT not initialized") }; lidt(idt, core::mem::size_of::<[GateDesc; 256]>() as usize); } From dc7eb6e4e1f38b5d1c072a70c1ff033cf6f8a620 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 07:54:00 +0000 Subject: [PATCH 45/69] Fixed rebase duplication bugs --- src/lib.rs | 13 ------------- src/traps.rs | 29 ----------------------------- 2 files changed, 42 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e70ea4..b521783 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,16 +70,3 @@ pub extern "C" fn entryofrust() -> ! { x86::wfi(); } } - -static mut PANICKED: bool = false; - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - // Disable interrupts to prevent interrupt handlers from interfering - cli(); - // Print panic message with LAPIC ID to identify which CPU panicked - println!("lapicid {}:\n{:#?}", lapicid(), info); - unsafe { PANICKED = true; } - // Halt the system - loop {} -} \ No newline at end of file diff --git a/src/traps.rs b/src/traps.rs index 274e301..646b1ea 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -47,35 +47,6 @@ impl GateDesc { static mut IDT: OnceCell<[GateDesc; 256]> = OnceCell::new(); pub static TICKS: AtomicU32 = AtomicU32::new(0); - -#[repr(C)] -pub struct TrapFrame { - // registers as pushed by pusha - pub edi: u32, - pub esi: u32, - pub ebp: u32, - pub oesp: u32, // useless & ignored - pub ebx: u32, - pub edx: u32, - pub ecx: u32, - pub eax: u32, - - pub trapno: u32, - - // below here defined by x86 hardware - pub err: u32, - pub eip: u32, - pub cs: u16, - pub padding5: u16, - pub eflags: u32, - - // below here only when crossing rings, such as from user to kernel - pub esp: u32, - pub ss: u16, - pub padding6: u16, -} - - pub fn tvinit() { let mut arr = [GateDesc::default(); 256]; for i in 0..256 { From 7d3d6dc18506144a8692c44ab56271c445d88c43 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 08:10:36 +0000 Subject: [PATCH 46/69] Minor bug fixes in traps --- src/console.rs | 2 +- src/traps.rs | 29 ----------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/console.rs b/src/console.rs index 507152b..61f0aa3 100644 --- a/src/console.rs +++ b/src/console.rs @@ -35,7 +35,7 @@ static mut INPUT: Input = Input { e: 0, }; -fn consputc(c: i32) { +pub fn consputc(c: i32) { if c == BACKSPACE { uartputc('\x08' as i32); uartputc(' ' as i32); diff --git a/src/traps.rs b/src/traps.rs index a1a2ee9..bce99fc 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -47,35 +47,6 @@ impl GateDesc { static mut IDT: OnceCell<[GateDesc; 256]> = OnceCell::new(); pub static TICKS: AtomicU32 = AtomicU32::new(0); - -#[repr(C)] -pub struct TrapFrame { - // registers as pushed by pusha - pub edi: u32, - pub esi: u32, - pub ebp: u32, - pub oesp: u32, // useless & ignored - pub ebx: u32, - pub edx: u32, - pub ecx: u32, - pub eax: u32, - - pub trapno: u32, - - // below here defined by x86 hardware - pub err: u32, - pub eip: u32, - pub cs: u16, - pub padding5: u16, - pub eflags: u32, - - // below here only when crossing rings, such as from user to kernel - pub esp: u32, - pub ss: u16, - pub padding6: u16, -} - - pub fn tvinit() { let mut arr = [GateDesc::default(); 256]; for i in 0..256 { From bf8b5266159abd9631ff5ae681e813b686214054 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 08:16:01 +0000 Subject: [PATCH 47/69] Makefile changes --- Makefile | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Makefile b/Makefile index a384fe0..36c7cb5 100644 --- a/Makefile +++ b/Makefile @@ -54,21 +54,12 @@ xv6.img: bootblock kernel dd if=bootblock of=xv6.img conv=notrunc dd if=kernel of=xv6.img seek=1 conv=notrunc -<<<<<<< HEAD # Build mkfs utility and create filesystem image mkfs: src/mkfs.rs rustc -W warnings -o mkfs src/mkfs.rs fs.img: mkfs *.txt ./mkfs fs.img *.txt -======= -# Second disk (fs.img) with welcome text (2 sectors, like C version) -fs: fs.img - -fs.img: welcome.txt - dd if=/dev/zero of=fs.img count=2 - dd if=welcome.txt of=fs.img conv=notrunc ->>>>>>> p6-ide bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c From 2c352cd7546524ae57858d6bcab6721927091aa8 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 09:02:37 +0000 Subject: [PATCH 48/69] Fixed welcome --- welcome.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/welcome.txt b/welcome.txt index 78ad1c5..5809c82 100644 --- a/welcome.txt +++ b/welcome.txt @@ -5,7 +5,3 @@ # ## # # # # # # # # # ## ## # # # # # # # # # ### # # ###### ###### #### #### # # ###### ### -<<<<<<< HEAD -======= - ->>>>>>> p7-mkfs From 05cc2a8d8d951ca607ffc5ec7bb900d3a1500a0b Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 09:26:09 +0000 Subject: [PATCH 49/69] Fixed static mut warnings in fs.rs --- src/fs.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index d5b783d..cb675e8 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -163,10 +163,11 @@ pub fn readsb(dev: u32, sb: &mut Superblock) { pub fn iinit(dev: u32) { unsafe { - readsb(dev, &mut SB); + readsb(dev, &mut *(&raw mut SB)); + let sb = &*(&raw const SB); println!( "sb: size {} nblocks {} ninodes {} nlog {} logstart {} inodestart {} bmap start {}", - SB.size, SB.nblocks, SB.ninodes, SB.nlog, SB.logstart, SB.inodestart, SB.bmapstart + sb.size, sb.nblocks, sb.ninodes, sb.nlog, sb.logstart, sb.inodestart, sb.bmapstart ); } } @@ -212,7 +213,7 @@ pub fn iread(idx: usize) { } if ip.valid == 0 { - let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); + let bp = bio::bread(ip.dev, iblock(ip.inum, &*(&raw const SB))); let data = &bio::buf_mut(bp).data; let off = (ip.inum % (IPB as u32)) as usize * DINODE_SIZE; From 0217dcd8fa827f2911a24fba4d1e2a95a27d7a6f Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 14:52:52 +0000 Subject: [PATCH 50/69] Updated LOGSIZE and FSSIZE --- src/Makefile | 130 ----------------------------------------------- src/constants.rs | 4 +- src/fs.rs | 42 +++++++-------- src/mkfs.rs | 4 +- 4 files changed, 21 insertions(+), 159 deletions(-) delete mode 100644 src/Makefile diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 079b0db..0000000 --- a/src/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -OBJS = entry.o vectors.o trapasm.o -RS = src/*.rs - -# Cross-compiling (e.g., on Mac OS X) -#TOOLPREFIX = i386-jos-elf -#TOOLPREFIX = i386-elf- - -# Using native tools (e.g., on X86 Linux) -#TOOLPREFIX = - -# 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; \ - then echo 'i386-jos-elf-'; \ - elif 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; \ - echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; \ - echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; \ - echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; \ - echo "*** environment variable to that prefix and run 'make' again." 1>&2; \ - echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ - echo "***" 1>&2; exit 1; fi) -endif - -# If the makefile can't find QEMU, specify its path here -# QEMU = qemu-system-i386 - -# Try to infer the correct QEMU -ifndef QEMU -QEMU = $(shell if which qemu > /dev/null; \ - then echo qemu; exit; \ - elif which qemu-system-i386 > /dev/null; \ - then echo qemu-system-i386; exit; \ - elif which qemu-system-x86_64 > /dev/null; \ - then echo qemu-system-x86_64; exit; \ - else \ - qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \ - if test -x $$qemu; then echo $$qemu; exit; fi; fi; \ - echo "***" 1>&2; \ - echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \ - echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \ - echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \ - echo "***" 1>&2; exit 1) -endif - -CC = $(TOOLPREFIX)gcc -AS = $(TOOLPREFIX)gas -LD = $(TOOLPREFIX)ld -OBJCOPY = $(TOOLPREFIX)objcopy -OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) -ASFLAGS = -m32 -gdwarf-2 -Wa,-divide -LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1) - -# Disable PIE when possible (for Ubuntu 16.10 toolchain) -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) -CFLAGS += -fno-pie -no-pie -endif -ifneq ($(shell $(CC) -dumpspecs 2>/dev/null | grep -e '[^f]nopie'),) -CFLAGS += -fno-pie -nopie -endif - -# Disk image with bootblock + kernel -xv6.img: bootblock kernel - dd if=/dev/zero of=xv6.img count=10000 - dd if=bootblock of=xv6.img conv=notrunc - dd if=kernel of=xv6.img seek=1 conv=notrunc - -mkfs: ../../col331/mkfs.c ../../col331/fs.h ../../col331/types.h ../../col331/stat.h ../../col331/param.h - gcc -Werror -Wall -o mkfs ../../col331/mkfs.c - -fs: fs.img - -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 - perl sign.pl bootblock - -kernel.a: $(RS) - 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 - $(OBJDUMP) -S -D kernel > kernel.asm - $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym - -vectors.S: vectors.pl - ./vectors.pl > vectors.S - -.PRECIOUS: %.o --include *.d - -clean: - rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ - *.a *.o *.d *.asm *.sym bootblock kernel xv6.img fs.img .gdbinit vectors.S mkfs - rm -rf target - -# run in emulators -GDBPORT = $(shell expr `id -u` % 5000 + 25000) -QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ - then echo "-gdb tcp::$(GDBPORT)"; \ - else echo "-s -p $(GDBPORT)"; fi) -ifndef CPUS - CPUS := 1 -endif - -# Attach fs.img as disk1 (index=1), like the C version -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 fs.img - $(QEMU) -nographic $(QEMUOPTS) - -.gdbinit: .gdbinit.tmpl - sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@ - -qemu-gdb: xv6.img fs .gdbinit - @echo "*** Now run 'gdb'." 1>&2 - $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB) diff --git a/src/constants.rs b/src/constants.rs index 40ea01c..fb6d9c4 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -146,9 +146,9 @@ pub const BPB: usize = BSIZE * 8; // bitmap bits per block pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk -pub const LOGSIZE: u32 = 30; // max data blocks in on-disk log (C version: 0, extended version: 30) +pub const LOGSIZE: u32 = 0; // max data blocks in on-disk log (C version: 0, extended version: 30) pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache -pub const FSSIZE: u32 = 10000; // size of file system in blocks (C version: 1000, extended: 10000) +pub const FSSIZE: u32 = 1000; // size of file system in blocks (C version: 1000, extended: 10000) pub const NINODES: u32 = 200; // number of inodes in file system // ------------------------------------------------------ FILE TYPES (stat.h) ---------------------------------------------- diff --git a/src/fs.rs b/src/fs.rs index 5811e92..76950a3 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -4,7 +4,7 @@ use crate::bio; use crate::buf::BSIZE; use crate::param::NINODE; use crate::println; -use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DINODE_SIZE, IPB}; +use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DINODE_SIZE, IPB, T_DIR}; pub use crate::constants::ROOTINO; pub const DIRENT_SIZE: usize = 2 + DIRSIZ; @@ -322,11 +322,8 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { } } -// ===================================================================== // Directories -// ===================================================================== - -/// Compare two directory entry names (up to DIRSIZ bytes). +/// Compare names up to DIRSIZ. pub fn namecmp(s: &[u8], t: &[u8]) -> bool { let slen = s.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(s.len())); let tlen = t.iter().take(DIRSIZ).position(|&b| b == 0).unwrap_or(DIRSIZ.min(t.len())); @@ -341,16 +338,16 @@ pub fn namecmp(s: &[u8], t: &[u8]) -> bool { true } -/// Look for a directory entry in a directory inode. -/// If found, return the inode cache index of the entry. -pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { +/// Look for a directory entry in a directory. +/// If found, set *poff to byte offset of entry. +pub fn dirlookup(dp_idx: usize, name: &[u8], poff: Option<&mut u32>) -> Option { unsafe { if dp_idx >= NINODE { panic!("dirlookup: bad inode index"); } let dp = &ICACHE.inode[dp_idx]; - if dp.type_ != T_DIR { + if (dp.type_ as u16) != T_DIR { panic!("dirlookup not DIR"); } @@ -371,6 +368,9 @@ pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { if namecmp(name, &de.name) { // entry matches path element + if let Some(poff_ref) = poff { + *poff_ref = off; + } let inum = de.inum as u32; return Some(iget(dp.dev, inum)); } @@ -381,13 +381,10 @@ pub fn dirlookup(dp_idx: usize, name: &[u8]) -> Option { } } -// ===================================================================== // Paths -// ===================================================================== /// Copy the next path element from path into name. -/// Return the remaining path after the element (with leading slashes stripped), -/// or None if there is no element to extract. +/// Return a pointer to the element following the copied one. fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { let mut i = 0; @@ -406,13 +403,11 @@ fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { } let len = i - start; - // Copy element into name if len >= DIRSIZ { name.copy_from_slice(&path[start..start + DIRSIZ]); } else { name[..len].copy_from_slice(&path[start..start + len]); name[len] = 0; - // Zero out rest of name for cleanliness for j in (len + 1)..DIRSIZ { name[j] = 0; } @@ -426,9 +421,8 @@ fn skipelem<'a>(path: &'a [u8], name: &mut [u8; DIRSIZ]) -> Option<&'a [u8]> { Some(&path[i..]) } -/// Look up and return the inode cache index for a path name. -/// If nameiparent_flag is true, return the inode for the parent and copy -/// the final path element into name. +/// Look up and return the inode for a path name. +/// If nameiparent != 0, return the inode for the parent. fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option { let mut ip = iget(crate::param::ROOTDEV, ROOTINO); @@ -439,7 +433,7 @@ fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option Some(rest) => { iread(ip); unsafe { - if ICACHE.inode[ip].type_ != T_DIR { + if (ICACHE.inode[ip].type_ as u16) != T_DIR { irelse(ip); return None; } @@ -451,7 +445,7 @@ fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option return Some(ip); } - match dirlookup(ip, name) { + match dirlookup(ip, name, None) { None => { irelse(ip); return None; @@ -473,16 +467,14 @@ fn namex(path: &[u8], nameiparent_flag: bool, name: &mut [u8; DIRSIZ]) -> Option Some(ip) } -/// Look up the inode cache index for a path name. -/// Mirrors C: struct inode* namei(char *path) +/// Look up the inode for a path name. pub fn namei(path: &[u8]) -> Option { let mut name = [0u8; DIRSIZ]; namex(path, false, &mut name) } -/// Look up the parent inode for a path name, and copy the final -/// path element into name. -/// Mirrors C: struct inode* nameiparent(char *path, char *name) +/// Look up the parent inode for a path name. +/// Copy the final path element into name. pub fn nameiparent(path: &[u8], name: &mut [u8; DIRSIZ]) -> Option { namex(path, true, name) } diff --git a/src/mkfs.rs b/src/mkfs.rs index 53fb795..f40ae26 100644 --- a/src/mkfs.rs +++ b/src/mkfs.rs @@ -35,9 +35,9 @@ const MAXFILE: usize = NDIRECT + NINDIRECT; // max file size in blocks const DIRSIZ: usize = 14; // directory name length // From param.h (System parameters) -const FSSIZE: u32 = 10_000; // size of file system in blocks +const FSSIZE: u32 = 1_000; // size of file system in blocks const NINODES: u32 = 200; // number of inodes -const LOGSIZE: u32 = 30; // max data blocks in on-disk log +const LOGSIZE: u32 = 0; // max data blocks in on-disk log // From stat.h (File types) const T_DIR: u16 = 1; // Directory From 58cb4c3029620f7d33fb5604fe77d5d4041e0c21 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 15:20:44 +0000 Subject: [PATCH 51/69] p9-name-layer: Add dirlookup poff parameter --- src/constants.rs | 1 + src/fs.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index fb6d9c4..e792a88 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -129,6 +129,7 @@ pub const MAXFILE: usize = NDIRECT + NINDIRECT; // max file size in blocks // Directory entry pub const DIRSIZ: usize = 14; // directory name length +pub const DIRENT_SIZE: usize = 2 + DIRSIZ; // sizeof(struct dirent) = 2 bytes for uint16_t + 14 bytes for name // Inode calculations // Note: In C these are macros. IPB = (BSIZE / sizeof(struct dinode)) diff --git a/src/fs.rs b/src/fs.rs index 76950a3..c43251c 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -4,10 +4,9 @@ use crate::bio; use crate::buf::BSIZE; use crate::param::NINODE; use crate::println; -use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DINODE_SIZE, IPB, T_DIR}; +use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, T_DIR}; pub use crate::constants::ROOTINO; -pub const DIRENT_SIZE: usize = 2 + DIRSIZ; #[derive(Copy, Clone)] #[repr(C)] From 3e2cd18e868cc1b17e46022225dcf1f01745991a Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 15:30:04 +0000 Subject: [PATCH 52/69] Updated trapasm.S --- trapasm.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trapasm.S b/trapasm.S index 414dc20..477a5ee 100644 --- a/trapasm.S +++ b/trapasm.S @@ -17,3 +17,5 @@ trapret: popal addl $0x8, %esp # trapno and errcode iret + +.section .note.GNU-stack,"",@progbits \ No newline at end of file From ca5789c8a5ecb59c2c53e90c1a7f146d1dbd3820 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 15:36:08 +0000 Subject: [PATCH 53/69] Updated .S files to remove ld GNU-stack warnings --- vectors.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vectors.pl b/vectors.pl index 57b49dd..27d419b 100755 --- a/vectors.pl +++ b/vectors.pl @@ -26,6 +26,8 @@ print " .long vector$i\n"; } +print ".section .note.GNU-stack,\"\",%progbits\n"; + # sample output: # # handlers # .globl alltraps From e4facf07ffbefe08684f8af949cfb856d99a33a9 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 20:13:10 +0000 Subject: [PATCH 54/69] Cleaned Constants usage, fixed static mut warnings --- src/fs.rs | 25 ++++++++++++------------- src/lib.rs | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index c2ad1eb..82b7d98 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -4,9 +4,10 @@ use crate::bio; use crate::buf::BSIZE; use crate::param::NINODE; use crate::println; -use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, T_DIR}; +use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, BPB, MAXFILE}; pub use crate::constants::ROOTINO; +pub use crate::constants::T_DIR; #[derive(Copy, Clone)] #[repr(C)] @@ -155,7 +156,7 @@ fn iblock(inum: u32, sb: &Superblock) -> u32 { #[inline] fn bblock(b: u32, sb: &Superblock) -> u32 { - b / BPB + sb.bmapstart + b / (BPB as u32) + sb.bmapstart } #[inline] @@ -211,12 +212,12 @@ fn balloc(dev: u32) -> u32 { unsafe { let mut b = 0u32; while b < SB.size { - let bp = bio::bread(dev, bblock(b, &SB)); + let bp = bio::bread(dev, bblock(b, &*(&raw const SB))); let mut found: Option = None; { let data = &mut bio::buf_mut(bp).data; let mut bi = 0u32; - while bi < BPB && b + bi < SB.size { + while bi < (BPB as u32) && b + bi < SB.size { let m: u8 = 1u8 << (bi % 8); let idx = (bi / 8) as usize; if (data[idx] & m) == 0 { @@ -236,7 +237,7 @@ fn balloc(dev: u32) -> u32 { } bio::brelse(bp); - b += BPB; + b += BPB as u32; } } @@ -247,8 +248,8 @@ pub fn ialloc(dev: u32, type_: i16) -> usize { unsafe { let mut inum = 1u32; while inum < SB.ninodes { - let bp = bio::bread(dev, iblock(inum, &SB)); - let off = (inum % IPB) as usize * DINODE_SIZE; + let bp = bio::bread(dev, iblock(inum, &*(&raw const SB))); + let off = (inum % (IPB as u32)) as usize * DINODE_SIZE; let free = { let data = &bio::buf_mut(bp).data; @@ -293,8 +294,8 @@ pub fn iupdate(idx: usize) { } let ip = &ICACHE.inode[idx]; - let bp = bio::bread(ip.dev, iblock(ip.inum, &SB)); - let off = (ip.inum % IPB) as usize * DINODE_SIZE; + let bp = bio::bread(ip.dev, iblock(ip.inum, &*(&raw const SB))); + let off = (ip.inum % (IPB as u32)) as usize * DINODE_SIZE; { let data = &mut bio::buf_mut(bp).data; @@ -534,10 +535,8 @@ pub fn namecmp(s: &str, t: &[u8; DIRSIZ]) -> bool { } pub fn dirlookup(dp_idx: usize, name: &str, mut poff: Option<&mut u32>) -> Option { - unsafe { - if dp_idx >= NINODE { - panic!("dirlookup: bad inode index"); - } + if dp_idx >= NINODE { + panic!("dirlookup: bad inode index"); } iread(dp_idx); diff --git a/src/lib.rs b/src/lib.rs index 7d5a715..71f9685 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ fn welcome() { Some(idx) => idx, None => { println!("/foo not found. Creating!"); - let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR); + let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR as i16); fs::iread(idx); let ino = fs::inode_inum(idx); if fs::dirlink(idx, ".", ino) < 0 { From 979d41bcd1044cfdf2c7f53d8f71c60d0d87e529 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 21:28:37 +0000 Subject: [PATCH 55/69] Updated constants, fix static mut warning --- src/file.rs | 33 +++++++++++++++++---------------- src/fs.rs | 4 ++-- src/lib.rs | 5 ++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/file.rs b/src/file.rs index e76f1f0..48e3213 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,6 +1,7 @@ use core::str; use crate::buf::BSIZE; +use crate::constants::{T_DIR, T_FILE, DIRSIZ, DIRENT_SIZE}; use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; use crate::fs; use crate::param::{MAXOPBLOCKS, NFILE}; @@ -48,8 +49,8 @@ impl FTable { static mut FTABLE: FTable = FTable::new(); -fn dirsiz_to_str(name: &[u8; fs::DIRSIZ]) -> &str { - let len = name.iter().position(|&b| b == 0).unwrap_or(fs::DIRSIZ); +fn dirsiz_to_str(name: &[u8; DIRSIZ]) -> &str { + let len = name.iter().position(|&b| b == 0).unwrap_or(DIRSIZ); str::from_utf8(&name[..len]).unwrap_or("") } @@ -218,23 +219,23 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { pub fn isdirempty(dp_idx: usize) -> bool { fs::iread(dp_idx); - let mut off = (2 * fs::DIRENT_SIZE) as u32; + let mut off = (2 * DIRENT_SIZE) as u32; while off < fs::inode_size(dp_idx) { - let mut raw = [0u8; fs::DIRENT_SIZE]; - if fs::readi(dp_idx, &mut raw, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + let mut raw = [0u8; DIRENT_SIZE]; + if fs::readi(dp_idx, &mut raw, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { panic!("isdirempty: readi"); } let de = fs::parse_dirent(&raw); if de.inum != 0 { return false; } - off += fs::DIRENT_SIZE as u32; + off += DIRENT_SIZE as u32; } true } -pub fn unlink(path: &str, name: &mut [u8; fs::DIRSIZ]) -> i32 { +pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { let dp = match fs::nameiparent(path, name) { Some(idx) => idx, None => return -1, @@ -264,18 +265,18 @@ pub fn unlink(path: &str, name: &mut [u8; fs::DIRSIZ]) -> i32 { panic!("unlink: nlink < 1"); } - if fs::inode_type(ip) == fs::T_DIR && !isdirempty(ip) { + if fs::inode_type(ip) == T_DIR && !isdirempty(ip) { fs::iput(ip); fs::iput(dp); return -1; } - let de = [0u8; fs::DIRENT_SIZE]; - if fs::writei(dp, &de, off, fs::DIRENT_SIZE as u32) != fs::DIRENT_SIZE as i32 { + let de = [0u8; DIRENT_SIZE]; + if fs::writei(dp, &de, off, DIRENT_SIZE as u32) != DIRENT_SIZE as i32 { panic!("unlink: writei"); } - if fs::inode_type(ip) == fs::T_DIR { + if fs::inode_type(ip) == T_DIR { fs::inode_dec_nlink(dp); fs::iupdate(dp); } @@ -289,7 +290,7 @@ pub fn unlink(path: &str, name: &mut [u8; fs::DIRSIZ]) -> i32 { } pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { - let mut name = [0u8; fs::DIRSIZ]; + let mut name = [0u8; DIRSIZ]; let dp = fs::nameiparent(path, &mut name)?; fs::iread(dp); @@ -299,7 +300,7 @@ pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { if let Some(ip) = fs::dirlookup(dp, name_str, None) { fs::iput(dp); fs::iread(ip); - if type_ == fs::T_FILE && fs::inode_type(ip) == fs::T_FILE { + if (type_ as u16) == T_FILE && fs::inode_type(ip) == T_FILE { return Some(ip); } fs::iput(ip); @@ -312,7 +313,7 @@ pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { fs::inode_set_meta(ip, major, minor, 1); fs::iupdate(ip); - if (type_ as u16) == fs::T_DIR { + if (type_ as u16) == T_DIR { fs::inode_inc_nlink(dp); fs::iupdate(dp); @@ -332,11 +333,11 @@ pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { pub fn open(path: &str, omode: i32) -> Option { let ip = if (omode & O_CREATE) != 0 { - create(path, fs::T_FILE, 0, 0)? + create(path, T_FILE as i16, 0, 0)? } else { let ip = fs::namei(path)?; fs::iread(ip); - if fs::inode_type(ip) == fs::T_DIR && omode != O_RDONLY { + if fs::inode_type(ip) == T_DIR && omode != O_RDONLY { fs::iput(ip); return None; } diff --git a/src/fs.rs b/src/fs.rs index 7b47e5d..3882a00 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -246,8 +246,8 @@ fn balloc(dev: u32) -> u32 { fn bfree(dev: u32, b: u32) { unsafe { - let bp = bio::bread(dev, bblock(b, &SB)); - let bi = b % BPB; + let bp = bio::bread(dev, bblock(b, &*(&raw const SB))); + let bi = b % (BPB as u32); let m: u8 = 1u8 << (bi % 8); let idx = (bi / 8) as usize; diff --git a/src/lib.rs b/src/lib.rs index 91eac26..44c0814 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,6 @@ mod mp; mod proc; mod traps; mod constants; // Internal use only - no external crates -mod fs; // Internal use only - filesystem structures mod buf; mod bio; mod ide; @@ -54,7 +53,7 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let _ = file::create("/foo", fs::T_DIR as i16, 0, 0); + let _ = file::create("/foo", constants::T_DIR as i16, 0, 0); let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) .unwrap_or_else(|| panic!("failed to create /foo/hello.txt")); @@ -71,7 +70,7 @@ fn welcome() { console::consputc('\n' as i32); file::fileclose(gtxt); - let mut name = [0u8; fs::DIRSIZ]; + let mut name = [0u8; constants::DIRSIZ]; if file::unlink("/foo/hello.txt", &mut name) < 0 { panic!("failed to unlink /foo/hello.txt"); } From 2c7f551f2fcfc2b223a1e92bb1cadf776c5c33ca Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 21:49:33 +0000 Subject: [PATCH 56/69] Removed KSTACKSIZE and NCPU redefinition (kept in param.rs) --- src/constants.rs | 5 ----- src/param.rs | 2 +- src/proc.rs | 3 ++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index e792a88..029c2af 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -17,11 +17,6 @@ pub const STS_T32A: u8 = 0x9; // Available 32-bit TSS pub const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate pub const STS_TG32: u8 = 0xF; // 32-bit Trap Gate - -// ----------------------------------------------------- CPU RELATED ---------------------------------------------- -pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack -pub const NCPU: usize = 8; // maximum number of CPUs - // ----------------------------------------------- TRAPS ------------------------------------------------------- // x86 trap and interrupt constants diff --git a/src/param.rs b/src/param.rs index a65eb45..207277e 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,4 +1,4 @@ -// pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack (unused in p3) +pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack (unused in p3) pub const NCPU: usize = 8; // maximum number of CPUs pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NFILE: usize = 100; // open files per system diff --git a/src/proc.rs b/src/proc.rs index 713ad58..f904002 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,7 +1,8 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs // use core::ptr; use crate::x86::readeflags; -use crate::constants::{FL_IF,NCPU}; +use crate::param::{NCPU}; +use crate::constants::{FL_IF}; use crate::lapic; #[derive(Debug, Clone, Copy)] From 31db167ccbee494064d0fea90f46712d1d754929 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 22:51:06 +0000 Subject: [PATCH 57/69] Incorporated log changes with file layer, updated welcome() to use mkdir --- src/constants.rs | 2 +- src/file.rs | 55 +++++++++++++++++++++++++++++--- src/fs.rs | 4 +-- src/lib.rs | 83 +++++++++++++++--------------------------------- src/mkfs.rs | 2 +- src/param.rs | 1 - 6 files changed, 80 insertions(+), 67 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 029c2af..e405f03 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -142,7 +142,7 @@ pub const BPB: usize = BSIZE * 8; // bitmap bits per block pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk -pub const LOGSIZE: u32 = 0; // max data blocks in on-disk log (C version: 0, extended version: 30) +pub const LOGSIZE: u32 = 30; // max data blocks in on-disk log (C version: 0, extended version: 30) pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache pub const FSSIZE: u32 = 1000; // size of file system in blocks (C version: 1000, extended: 10000) pub const NINODES: u32 = 200; // number of inodes in file system diff --git a/src/file.rs b/src/file.rs index 48e3213..8f2d079 100644 --- a/src/file.rs +++ b/src/file.rs @@ -108,7 +108,9 @@ pub fn fileclose(f_idx: usize) { } if ff.type_ == FileType::Inode { + crate::log::begin_op(); fs::iput(ff.ip); + crate::log::end_op(); } } @@ -188,6 +190,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { n1 = max; } + crate::log::begin_op(); fs::iread(f.ip); let off = unsafe { FTABLE.file[f_idx].off }; let r = fs::writei(f.ip, &src[i as usize..], off, n1 as u32); @@ -196,6 +199,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { FTABLE.file[f_idx].off += r as u32; } } + crate::log::end_op(); if r < 0 { break; @@ -236,9 +240,14 @@ pub fn isdirempty(dp_idx: usize) -> bool { } pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { + crate::log::begin_op(); + let dp = match fs::nameiparent(path, name) { Some(idx) => idx, - None => return -1, + None => { + crate::log::end_op(); + return -1; + } }; fs::iread(dp); @@ -247,6 +256,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if name_str == "." || name_str == ".." { fs::iput(dp); + crate::log::end_op(); return -1; } @@ -255,6 +265,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { Some(idx) => idx, None => { fs::iput(dp); + crate::log::end_op(); return -1; } }; @@ -268,6 +279,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if fs::inode_type(ip) == T_DIR && !isdirempty(ip) { fs::iput(ip); fs::iput(dp); + crate::log::end_op(); return -1; } @@ -286,10 +298,11 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { fs::iupdate(ip); fs::iput(ip); + crate::log::end_op(); 0 } -pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { +fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { let mut name = [0u8; DIRSIZ]; let dp = fs::nameiparent(path, &mut name)?; @@ -332,13 +345,29 @@ pub fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { } pub fn open(path: &str, omode: i32) -> Option { + crate::log::begin_op(); + let ip = if (omode & O_CREATE) != 0 { - create(path, T_FILE as i16, 0, 0)? + let ip = match create(path, T_FILE as i16, 0, 0) { + Some(ip) => ip, + None => { + crate::log::end_op(); + return None; + } + }; + ip } else { - let ip = fs::namei(path)?; + let ip = match fs::namei(path) { + Some(ip) => ip, + None => { + crate::log::end_op(); + return None; + } + }; fs::iread(ip); if fs::inode_type(ip) == T_DIR && omode != O_RDONLY { fs::iput(ip); + crate::log::end_op(); return None; } ip @@ -348,6 +377,7 @@ pub fn open(path: &str, omode: i32) -> Option { Some(idx) => idx, None => { fs::iput(ip); + crate::log::end_op(); return None; } }; @@ -361,5 +391,22 @@ pub fn open(path: &str, omode: i32) -> Option { f.writable = (omode & O_WRONLY) != 0 || (omode & O_RDWR) != 0; } + crate::log::end_op(); Some(f_idx) } + +pub fn mkdir(path: &str) -> i32 { + crate::log::begin_op(); + + let ip = match create(path, T_DIR as i16, 0, 0) { + Some(ip) => ip, + None => { + crate::log::end_op(); + return -1; + } + }; + + fs::iput(ip); + crate::log::end_op(); + 0 +} diff --git a/src/fs.rs b/src/fs.rs index 709ff18..95fbff5 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -6,8 +6,8 @@ use crate::log; use crate::param::NINODE; use crate::println; use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, BPB, MAXFILE}; +use crate::constants::{ROOTDEV, ROOTINO}; -pub use crate::constants::ROOTINO; pub use crate::constants::T_DIR; #[derive(Copy, Clone)] @@ -260,7 +260,7 @@ fn bfree(dev: u32, b: u32) { data[idx] &= !m; } - bio::bwrite(bp); + log::log_write(bp); bio::brelse(bp); } } diff --git a/src/lib.rs b/src/lib.rs index 8594906..211a050 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ mod buf; mod bio; mod ide; mod fs; +mod fcntl; +mod file; mod log; use crate::traps::*; @@ -52,77 +54,42 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let root = fs::namei("/").unwrap_or_else(|| panic!("root not found")); - fs::iread(root); - let foodir = match fs::dirlookup(root, "foo", None) { - Some(idx) => idx, - None => { - log::begin_op(); - println!("/foo not found. Creating!"); - let idx = fs::ialloc(param::ROOTDEV, fs::T_DIR as i16); - fs::iread(idx); - let ino = fs::inode_inum(idx); - if fs::dirlink(idx, ".", ino) < 0 { - panic!("failed to link . in /foo"); - } - if fs::dirlink(idx, "..", ino) < 0 { - panic!("failed to link .. in /foo"); - } - if fs::dirlink(root, "foo", ino) < 0 { - panic!("failed to link /foo in root"); - } - log::end_op(); - idx - } - }; - - let wtxt = match fs::namei("/foo/greet.txt") { - Some(idx) => idx, - None => { - log::begin_op(); - println!("/foo/greet.txt not found. Creating!"); - let wtxt_orig = - fs::namei("/welcome.txt").unwrap_or_else(|| panic!("/welcome.txt missing")); - let inum = fs::inode_inum(wtxt_orig); - if fs::dirlink(foodir, "greet.txt", inum) < 0 { - panic!("failed to link greet.txt in /foo"); - } - fs::irelease(wtxt_orig); - log::end_op(); - fs::namei("/foo/greet.txt").unwrap_or_else(|| panic!("greet.txt lookup failed")) - } - }; - + // Create and write /foo/hello.txt + file::mkdir("/foo"); + + let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) + .unwrap_or_else(|| panic!("Failed to create /foo/hello.txt")); + let n = file::filewrite(gtxt, b"hello\0", 6); + println!("Wrote {} characters to /foo/hello.txt", n); + file::fileclose(gtxt); + let gtxt = file::open("/foo/hello.txt", fcntl::O_RDONLY) - .unwrap_or_else(|| panic!("unable to open /foo/hello.txt")); + .unwrap_or_else(|| panic!("Unable to open /foo/hello.txt")); let mut welcome = [0u8; 512]; let n = file::fileread(gtxt, &mut welcome, 6); println!("Read {} chars from /foo/hello.txt: ", n); print_cstr(&welcome); console::consputc('\n' as i32); file::fileclose(gtxt); - + + // Delete /foo/hello.txt let mut name = [0u8; constants::DIRSIZ]; - if file::unlink("/foo/hello.txt", &mut name) < 0 { - panic!("failed to unlink /foo/hello.txt"); - } - + file::unlink("/foo/hello.txt", &mut name); + let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); if !file::isdirempty(foo) { panic!("/foo should be empty"); } - fs::iput(foo); - - if let Some(f) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { - file::fileclose(f); - panic!("could open /foo/hello.txt after unlinking"); + + if let Some(_gtxt) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { + panic!("Could open /foo/hello.txt after unlinking"); } - - let wtxt = - file::open("/welcome.txt", fcntl::O_RDONLY).unwrap_or_else(|| panic!("unable to open /welcome.txt")); - let welcome_cap = welcome.len() as i32; - let n = file::fileread(wtxt, &mut welcome, welcome_cap); - println!("Read {} chars from /welcome.txt:", n); + + // Print welcome message + let wtxt = file::open("/welcome.txt", fcntl::O_RDONLY) + .unwrap_or_else(|| panic!("Unable to open /welcome.txt")); + let n = file::fileread(wtxt, &mut welcome, 512); + println!("Read {} chars from /welcome.txt:\n", n); print_cstr(&welcome); file::fileclose(wtxt); } diff --git a/src/mkfs.rs b/src/mkfs.rs index f40ae26..6289e66 100644 --- a/src/mkfs.rs +++ b/src/mkfs.rs @@ -37,7 +37,7 @@ const DIRSIZ: usize = 14; // directory name length // From param.h (System parameters) const FSSIZE: u32 = 1_000; // size of file system in blocks const NINODES: u32 = 200; // number of inodes -const LOGSIZE: u32 = 0; // max data blocks in on-disk log +const LOGSIZE: u32 = 30; // max data blocks in on-disk log // From stat.h (File types) const T_DIR: u16 = 1; // Directory diff --git a/src/param.rs b/src/param.rs index b951afa..56333d0 100644 --- a/src/param.rs +++ b/src/param.rs @@ -4,5 +4,4 @@ pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes pub const ROOTDEV: u32 = 1; // device number of file system root disk -pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log From 29e2be0a10eb9dc6923af00f262f18c11f3a2d9a Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 23:01:45 +0000 Subject: [PATCH 58/69] Updated overlapping constants between constants.rs and param.rs --- src/constants.rs | 6 +----- src/fs.rs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index e405f03..618fa7b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -138,11 +138,7 @@ pub const BPB: usize = BSIZE * 8; // bitmap bits per block // ------------------------------------------------------ SYSTEM PARAMETERS (param.h) ---------------------------------------------- // System-wide parameters - -pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes -pub const NINODE: usize = 50; // maximum number of active i-nodes -pub const ROOTDEV: u32 = 1; // device number of file system root disk -pub const LOGSIZE: u32 = 30; // max data blocks in on-disk log (C version: 0, extended version: 30) +pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache pub const FSSIZE: u32 = 1000; // size of file system in blocks (C version: 1000, extended: 10000) pub const NINODES: u32 = 200; // number of inodes in file system diff --git a/src/fs.rs b/src/fs.rs index 95fbff5..6d56bdd 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -3,10 +3,10 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; use crate::log; -use crate::param::NINODE; +use crate::param::{NINODE, ROOTDEV}; use crate::println; use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, BPB, MAXFILE}; -use crate::constants::{ROOTDEV, ROOTINO}; +use crate::constants::{ROOTINO}; pub use crate::constants::T_DIR; From 5fb8679b1b9334177218ec1bb7f71f49fdfca780 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Tue, 3 Mar 2026 23:14:07 +0000 Subject: [PATCH 59/69] Undo README changes --- README.md | 51 +-------------------------------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/README.md b/README.md index 1305e65..dddf7be 100644 --- a/README.md +++ b/README.md @@ -1,50 +1 @@ -# 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 - +# col331rs: p12-file-layer (Rust) \ No newline at end of file From 64f2e05bd991353bfa26cef23bc34e8d70f2bbe9 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 10:22:05 +0000 Subject: [PATCH 60/69] Implemented device virtual file system --- src/console.rs | 59 ++++++++++++++++++++++++++++++++++++++ src/file.rs | 78 ++++++++++++++++++++++++++++++++++++-------------- src/fs.rs | 23 +++++++++++++-- src/lib.rs | 45 +++++++---------------------- src/param.rs | 2 ++ 5 files changed, 150 insertions(+), 57 deletions(-) diff --git a/src/console.rs b/src/console.rs index 69b0669..cd4b939 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,5 +1,7 @@ use crate::uart::*; use core::fmt::*; +use crate::file::DEVSW; +use crate::param::CONSOLE; pub struct Console {} impl Write for Console { @@ -82,3 +84,60 @@ pub fn consoleintr(getc: fn() -> i32) { } } } + +pub fn consoleread(_ip: usize, dst: &mut [u8], n: i32) -> i32 { + let target = n; + let mut n = n; + + unsafe { + let input = &raw mut INPUT; + while n > 0 { + // Busy wait for input - mirrors C: while(input.r == input.w); + while (*input).r == (*input).w { + // Block until data is available + } + + // Read character and increment read pointer - mirrors C: input.buf[input.r++ % INPUT_BUF] + let c = (*input).buf[(*input).r % INPUT_BUF] as i32; + (*input).r += 1; + + // Handle EOF (Ctrl-D) + if c == CTRL_D { + if n < target { + // Save ^D for next time, to make sure + // caller gets a 0-byte result. + (*input).r -= 1; + } + break; + } + + // Copy character to destination - mirrors C: *dst++ = c; + dst[(target - n) as usize] = c as u8; + n -= 1; + + // Break on newline + if c == '\n' as i32 { + break; + } + } + } + + target - n +} + +pub fn consolewrite(_ip: usize, src: &[u8], n: i32) -> i32 { + // Mirrors C: for(i = 0; i < n; i++) consputc(buf[i] & 0xff); + for i in 0..n { + consputc(src[i as usize] as i32); + } + n +} + +pub fn consoleinit() { + // Register console device handlers in the device switch table + // Mirrors C: devsw[CONSOLE].write = consolewrite; devsw[CONSOLE].read = consoleread; + unsafe { + DEVSW[CONSOLE].read = Some(consoleread); + DEVSW[CONSOLE].write = Some(consolewrite); + } +} diff --git a/src/file.rs b/src/file.rs index 8f2d079..427448b 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,15 +1,35 @@ use core::str; use crate::buf::BSIZE; -use crate::constants::{T_DIR, T_FILE, DIRSIZ, DIRENT_SIZE}; +use crate::constants::{T_DIR, T_FILE, T_DEV, DIRSIZ, DIRENT_SIZE}; use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; use crate::fs; -use crate::param::{MAXOPBLOCKS, NFILE}; +use crate::log; +use crate::param::{MAXOPBLOCKS, NFILE, NDEV}; + +// Device switch table entry +#[derive(Copy, Clone)] +pub struct Devsw { + pub read: Option i32>, + pub write: Option i32>, +} + +impl Devsw { + pub const fn new() -> Self { + Self { + read: None, + write: None, + } + } +} + +pub static mut DEVSW: [Devsw; NDEV] = [const { Devsw::new() }; NDEV]; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FileType { None, Inode, + Device, } #[derive(Copy, Clone)] @@ -108,9 +128,9 @@ pub fn fileclose(f_idx: usize) { } if ff.type_ == FileType::Inode { - crate::log::begin_op(); + log::begin_op(); fs::iput(ff.ip); - crate::log::end_op(); + log::end_op(); } } @@ -190,7 +210,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { n1 = max; } - crate::log::begin_op(); + log::begin_op(); fs::iread(f.ip); let off = unsafe { FTABLE.file[f_idx].off }; let r = fs::writei(f.ip, &src[i as usize..], off, n1 as u32); @@ -199,7 +219,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { FTABLE.file[f_idx].off += r as u32; } } - crate::log::end_op(); + log::end_op(); if r < 0 { break; @@ -240,12 +260,12 @@ pub fn isdirempty(dp_idx: usize) -> bool { } pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { - crate::log::begin_op(); + log::begin_op(); let dp = match fs::nameiparent(path, name) { Some(idx) => idx, None => { - crate::log::end_op(); + log::end_op(); return -1; } }; @@ -256,7 +276,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if name_str == "." || name_str == ".." { fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } @@ -265,7 +285,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { Some(idx) => idx, None => { fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } }; @@ -279,7 +299,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if fs::inode_type(ip) == T_DIR && !isdirempty(ip) { fs::iput(ip); fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } @@ -298,7 +318,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { fs::iupdate(ip); fs::iput(ip); - crate::log::end_op(); + log::end_op(); 0 } @@ -345,13 +365,13 @@ fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { } pub fn open(path: &str, omode: i32) -> Option { - crate::log::begin_op(); + log::begin_op(); let ip = if (omode & O_CREATE) != 0 { let ip = match create(path, T_FILE as i16, 0, 0) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); return None; } }; @@ -360,14 +380,14 @@ pub fn open(path: &str, omode: i32) -> Option { let ip = match fs::namei(path) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); return None; } }; fs::iread(ip); if fs::inode_type(ip) == T_DIR && omode != O_RDONLY { fs::iput(ip); - crate::log::end_op(); + log::end_op(); return None; } ip @@ -377,7 +397,7 @@ pub fn open(path: &str, omode: i32) -> Option { Some(idx) => idx, None => { fs::iput(ip); - crate::log::end_op(); + log::end_op(); return None; } }; @@ -391,22 +411,38 @@ pub fn open(path: &str, omode: i32) -> Option { f.writable = (omode & O_WRONLY) != 0 || (omode & O_RDWR) != 0; } - crate::log::end_op(); + log::end_op(); Some(f_idx) } pub fn mkdir(path: &str) -> i32 { - crate::log::begin_op(); + log::begin_op(); let ip = match create(path, T_DIR as i16, 0, 0) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); + return -1; + } + }; + + fs::iput(ip); + log::end_op(); + 0 +} + +pub fn mknod(path: &str, major: i16, minor: i16) -> i32 { + log::begin_op(); + + let ip = match create(path, T_DEV as i16, major, minor) { + Some(ip) => ip, + None => { + log::end_op(); return -1; } }; fs::iput(ip); - crate::log::end_op(); + log::end_op(); 0 } diff --git a/src/fs.rs b/src/fs.rs index 6d56bdd..12dcf4c 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -3,10 +3,11 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; use crate::log; -use crate::param::{NINODE, ROOTDEV}; +use crate::param::{NINODE, ROOTDEV, NDEV}; use crate::println; use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, BPB, MAXFILE}; -use crate::constants::{ROOTINO}; +use crate::constants::{ROOTINO, T_DEV}; +use crate::file::DEVSW; pub use crate::constants::T_DIR; @@ -516,6 +517,15 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; + + // Handle device files + if ip.type_ == T_DEV as i16 { + if ip.major < 0 || (ip.major as usize) >= NDEV || DEVSW[ip.major as usize].read.is_none() { + return -1; + } + return DEVSW[ip.major as usize].read.unwrap()(idx, dst, n as i32); + } + if off > ip.size || off.checked_add(n).is_none() || ip.nlink < 1 { return -1; } @@ -557,6 +567,15 @@ pub fn writei(idx: usize, src: &[u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; + + // Handle device files + if ip.type_ == T_DEV as i16 { + if ip.major < 0 || (ip.major as usize) >= NDEV || DEVSW[ip.major as usize].write.is_none() { + return -1; + } + return DEVSW[ip.major as usize].write.unwrap()(idx, src, n as i32); + } + if off > ip.size || off.checked_add(n).is_none() { return -1; } diff --git a/src/lib.rs b/src/lib.rs index 211a050..133cbc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,44 +54,19 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - // Create and write /foo/hello.txt - file::mkdir("/foo"); + let c = file::open("console", fcntl::O_RDWR) + .unwrap_or_else(|| panic!("Failed to open console")); - let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) - .unwrap_or_else(|| panic!("Failed to create /foo/hello.txt")); - let n = file::filewrite(gtxt, b"hello\0", 6); - println!("Wrote {} characters to /foo/hello.txt", n); - file::fileclose(gtxt); + file::filewrite(c, b"\nEnter your name: ", 18); - let gtxt = file::open("/foo/hello.txt", fcntl::O_RDONLY) - .unwrap_or_else(|| panic!("Unable to open /foo/hello.txt")); - let mut welcome = [0u8; 512]; - let n = file::fileread(gtxt, &mut welcome, 6); - println!("Read {} chars from /foo/hello.txt: ", n); - print_cstr(&welcome); - console::consputc('\n' as i32); - file::fileclose(gtxt); + let mut name = [0u8; 20]; + let namelen = file::fileread(c, &mut name, 20); - // Delete /foo/hello.txt - let mut name = [0u8; constants::DIRSIZ]; - file::unlink("/foo/hello.txt", &mut name); + file::filewrite(c, b"Nice to meet you! ", 18); + file::filewrite(c, &name[..namelen as usize], namelen); + file::filewrite(c, b"BYE!\n", 6); - let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); - if !file::isdirempty(foo) { - panic!("/foo should be empty"); - } - - if let Some(_gtxt) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { - panic!("Could open /foo/hello.txt after unlinking"); - } - - // Print welcome message - let wtxt = file::open("/welcome.txt", fcntl::O_RDONLY) - .unwrap_or_else(|| panic!("Unable to open /welcome.txt")); - let n = file::fileread(wtxt, &mut welcome, 512); - println!("Read {} chars from /welcome.txt:\n", n); - print_cstr(&welcome); - file::fileclose(wtxt); + file::fileclose(c); } extern "C" { @@ -104,6 +79,7 @@ pub extern "C" fn entryofrust() -> ! { lapic::lapicinit(); picirq::picinit(); ioapic::ioapic_init(); + console::consoleinit(); uart::uartinit(); ide::ideinit(); tvinit(); @@ -112,6 +88,7 @@ pub extern "C" fn entryofrust() -> ! { x86::sti(); fs::iinit(param::ROOTDEV); log::initlog(param::ROOTDEV); + file::mknod("console", param::CONSOLE as i16, param::CONSOLE as i16); welcome(); loop { diff --git a/src/param.rs b/src/param.rs index 56333d0..4a269c3 100644 --- a/src/param.rs +++ b/src/param.rs @@ -3,5 +3,7 @@ pub const NCPU: usize = 8; // maximum number of CPUs pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const NDEV: usize = 10; // maximum major device number pub const ROOTDEV: u32 = 1; // device number of file system root disk pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log +pub const CONSOLE: usize = 1; // console device number From 03cd9c8d963087e5a456b8b0aa772f2a89469395 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 11:42:37 +0000 Subject: [PATCH 61/69] Fixed Consoleread to use volatile reads --- src/console.rs | 14 ++++++++------ src/lib.rs | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/console.rs b/src/console.rs index cd4b939..ad6ce9a 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,4 @@ -use crate::uart::*; +use crate::{println, uart::*}; use core::fmt::*; use crate::file::DEVSW; use crate::param::CONSOLE; @@ -71,13 +71,14 @@ pub fn consoleintr(getc: fn() -> i32) { } _ => { if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { + let orig_c = c; let c = if c == '\r' as i32 { '\n' as i32 } else { c }; (*input).buf[(*input).e % INPUT_BUF] = c as u8; (*input).e += 1; consputc(c); if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { (*input).w = (*input).e; - } + } } } } @@ -88,15 +89,16 @@ pub fn consoleintr(getc: fn() -> i32) { pub fn consoleread(_ip: usize, dst: &mut [u8], n: i32) -> i32 { let target = n; let mut n = n; - + unsafe { let input = &raw mut INPUT; while n > 0 { // Busy wait for input - mirrors C: while(input.r == input.w); - while (*input).r == (*input).w { - // Block until data is available + while core::ptr::read_volatile(&(*input).r) == core::ptr::read_volatile(&(*input).w) { + // Spin-wait (busy wait) for input to arrive + core::hint::spin_loop(); } - + // Read character and increment read pointer - mirrors C: input.buf[input.r++ % INPUT_BUF] let c = (*input).buf[(*input).r % INPUT_BUF] as i32; (*input).r += 1; diff --git a/src/lib.rs b/src/lib.rs index 133cbc6..e5cedbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,17 +54,26 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let c = file::open("console", fcntl::O_RDWR) - .unwrap_or_else(|| panic!("Failed to open console")); + // Use println! to verify we reach this point (goes via Console::Write, not file) + + let c = match file::open("/console", fcntl::O_RDWR) { + Some(fd) => { + fd + } + None => { + panic!("Failed to open console"); + } + }; - file::filewrite(c, b"\nEnter your name: ", 18); + let n = file::filewrite(c, b"\nEnter your name: ", 18); let mut name = [0u8; 20]; + let nice_message = b"Nice to meet you! "; + let bye_message = b"BYE!\n"; let namelen = file::fileread(c, &mut name, 20); - - file::filewrite(c, b"Nice to meet you! ", 18); + file::filewrite(c, nice_message, nice_message.len() as i32); file::filewrite(c, &name[..namelen as usize], namelen); - file::filewrite(c, b"BYE!\n", 6); + file::filewrite(c, bye_message, bye_message.len() as i32); // Goodbye message is 5 bytes not 6 (Rust vs C string handling) file::fileclose(c); } @@ -88,7 +97,7 @@ pub extern "C" fn entryofrust() -> ! { x86::sti(); fs::iinit(param::ROOTDEV); log::initlog(param::ROOTDEV); - file::mknod("console", param::CONSOLE as i16, param::CONSOLE as i16); + file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); welcome(); loop { From 776250f8c59d21d7def200312c2ac4ad960d34bc Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 11:46:44 +0000 Subject: [PATCH 62/69] Restored missing enter_message --- src/console.rs | 5 ++--- src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/console.rs b/src/console.rs index ad6ce9a..7ad6727 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,4 @@ -use crate::{println, uart::*}; +use crate::{uart::*}; use core::fmt::*; use crate::file::DEVSW; use crate::param::CONSOLE; @@ -71,7 +71,6 @@ pub fn consoleintr(getc: fn() -> i32) { } _ => { if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { - let orig_c = c; let c = if c == '\r' as i32 { '\n' as i32 } else { c }; (*input).buf[(*input).e % INPUT_BUF] = c as u8; (*input).e += 1; @@ -89,7 +88,7 @@ pub fn consoleintr(getc: fn() -> i32) { pub fn consoleread(_ip: usize, dst: &mut [u8], n: i32) -> i32 { let target = n; let mut n = n; - + unsafe { let input = &raw mut INPUT; while n > 0 { diff --git a/src/lib.rs b/src/lib.rs index e5cedbe..0795bed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,8 +64,9 @@ fn welcome() { panic!("Failed to open console"); } }; - - let n = file::filewrite(c, b"\nEnter your name: ", 18); + + let enter_message = b"\nEnter your name: "; + file::filewrite(c, enter_message, enter_message.len() as i32); let mut name = [0u8; 20]; let nice_message = b"Nice to meet you! "; From 9aa40097ac12c71a5996d3f3f14f74d84a7a4229 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 18:09:20 +0000 Subject: [PATCH 63/69] Add x86 memory segmentation support --- src/constants.rs | 13 +++++++++++++ src/lib.rs | 3 +++ src/mmu.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/proc.rs | 31 ++++++++----------------------- src/vm.rs | 23 +++++++++++++++++++++++ src/x86.rs | 28 +++++++++++++++++----------- 6 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 src/mmu.rs create mode 100644 src/vm.rs diff --git a/src/constants.rs b/src/constants.rs index 618fa7b..f7dd337 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,6 +12,19 @@ pub const SEG_UCODE: u16 = 3; // user code pub const SEG_UDATA: u16 = 4; // user data+stack pub const SEG_TSS: u16 = 5; // this process's task state +// cpu->gdt[NSEGS] holds the above segments. +pub const NSEGS: usize = 6; + +// Application segment type bits +pub const STA_X: u8 = 0x8; // Executable segment +pub const STA_W: u8 = 0x2; // Writeable (non-executable segments) +pub const STA_R: u8 = 0x2; // Readable (executable segments) + +// Memory layout +// We assume that kernel.asm can fit in first 2MB +pub const STARTPROC: u32 = 0x200000; // Start allocating process from here (2MB) +pub const PROCSIZE: u32 = 0x100000; // Size of each process (1MB) + // System segment type bits pub const STS_T32A: u8 = 0x9; // Available 32-bit TSS pub const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate diff --git a/src/lib.rs b/src/lib.rs index 0795bed..a318bef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,8 @@ mod fs; mod fcntl; mod file; mod log; +mod mmu; +mod vm; use crate::traps::*; #[macro_export] @@ -99,6 +101,7 @@ pub extern "C" fn entryofrust() -> ! { fs::iinit(param::ROOTDEV); log::initlog(param::ROOTDEV); file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); + vm::seginit(); // segment descriptors welcome(); loop { diff --git a/src/mmu.rs b/src/mmu.rs new file mode 100644 index 0000000..505b33a --- /dev/null +++ b/src/mmu.rs @@ -0,0 +1,46 @@ +#![allow(unused_parens)] // False positive from bitfield macro + +use modular_bitfield::prelude::*; + +// Segment Descriptor + +#[bitfield] +#[repr(C, packed)] +#[derive(Clone, Copy, Default, Debug)] +pub struct SegDesc { + lim_15_0: B16, // Low bits of segment limit + base_15_0: B16, // Low bits of segment base address + base_23_16: B8, // Middle bits of segment base address + seg_type: B4, // Segment type (see STA_ constants) + s: B1, // 0 = system, 1 = application + dpl: B2, // Descriptor Privilege Level + p: B1, // Present + lim_19_16: B4, // High bits of segment limit + avl: B1, // Unused (available for software use) + rsv1: B1, // Reserved + db: B1, // 0 = 16-bit segment, 1 = 32-bit segment + g: B1, // Granularity: limit scaled by 4K when set + base_31_24: B8, // High bits of segment base address +} + +impl SegDesc { + /// Create a normal segment descriptor + /// Matches the C macro: SEG(type, base, lim, dpl) + pub fn seg(seg_type: u8, base: u32, lim: u32, dpl: u8) -> Self { + let mut seg = SegDesc::default(); + seg.set_lim_15_0(((lim >> 12) & 0xffff) as u16); + seg.set_base_15_0((base & 0xffff) as u16); + seg.set_base_23_16(((base >> 16) & 0xff) as u8); + seg.set_seg_type(seg_type); + seg.set_s(1); + seg.set_dpl(dpl); + seg.set_p(1); + seg.set_lim_19_16((lim >> 28) as u8); + seg.set_avl(0); + seg.set_rsv1(0); + seg.set_db(1); + seg.set_g(1); + seg.set_base_31_24(((base >> 24) & 0xff) as u8); + seg + } +} diff --git a/src/proc.rs b/src/proc.rs index f904002..44c0fad 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,18 +1,20 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs // use core::ptr; -use crate::x86::readeflags; -use crate::param::{NCPU}; -use crate::constants::{FL_IF}; -use crate::lapic; +use crate::constants::NSEGS; +use crate::mmu::SegDesc; #[derive(Debug, Clone, Copy)] pub struct Cpu { pub apicid: u8, // Local APIC ID + pub gdt: [SegDesc; NSEGS], // x86 global descriptor table } impl Cpu { pub const fn new() -> Self { - Self { apicid: 0 } + Self { + apicid: 0, + gdt: [SegDesc::new(); NSEGS], + } } } @@ -22,23 +24,6 @@ pub fn cpuid() -> usize { } pub fn mycpu() -> &'static Cpu { - let apicid: usize; - let mut i: usize = 0; - - if readeflags() & FL_IF != 0 { - panic!("mycpu called with interrupts enabled\n"); - } - - apicid = lapic::lapicid() as usize; - - // Access the cpus array via MP_ONCE let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); - - while i < NCPU { - if (cpus[i].apicid as usize) == apicid { - return &cpus[i]; - } - i += 1; - } - panic!("unknown apicid\n"); + &cpus[0] } \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..20baffb --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,23 @@ +use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE}; +use crate::mmu::SegDesc; +use crate::proc::cpuid; +use crate::mp::MP_ONCE; +use crate::x86::lgdt; +use core::mem::size_of_val; + +/// Set up CPU's kernel segment descriptors. +/// Run once on entry on each CPU. +pub fn seginit() { + unsafe { + // Map "logical" addresses to virtual addresses using identity map. + let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); + let cpu_ptr = cpus.as_ptr() as *mut crate::proc::Cpu; + let c = &mut *cpu_ptr.add(cpuid()); + + c.gdt[SEG_KCODE as usize] = SegDesc::seg(STA_X | STA_R, 0, 0xffffffff, 0); + c.gdt[SEG_KDATA as usize] = SegDesc::seg(STA_W, 0, 0xffffffff, 0); + c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, PROCSIZE, 0); + c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, PROCSIZE, 0); + lgdt(&c.gdt, size_of_val(&c.gdt)); + } +} diff --git a/src/x86.rs b/src/x86.rs index e8d8052..248683d 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -1,5 +1,7 @@ use core::arch::asm; use crate::traps::GateDesc; +use crate::mmu::SegDesc; +use crate::constants::NSEGS; pub fn inb(port: u16) -> u8 { let result: u8; @@ -36,17 +38,6 @@ pub fn outw(port: u16, value: u16) { } } -pub fn readeflags() -> u32 { - unsafe { - let eflags: u32; - asm!( - "pushfd; pop eax", - out("eax") eflags, - options(nomem, nostack) - ); - eflags - } -} pub fn cli () { unsafe { @@ -75,6 +66,21 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn lgdt(gdt: *const [SegDesc; NSEGS], size: usize) { + let pd: [u16; 3] = [ + (size - 1) as u16, + (gdt as *const _) as u16, + ((gdt as *const _ as u32) >> 16) as u16, + ]; + unsafe { + asm!( + "lgdt [{0:e}]", + in(reg) (&pd as *const _ ) as u32, + options(nostack, readonly) + ); + } +} + pub fn noop() { core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); } From 8b026ce96c614adc4e525c24cf24c5e51ae42b59 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 21:26:52 +0000 Subject: [PATCH 64/69] First process execution --- .gdbinit.tmpl | 2 +- Makefile | 12 ++- initcode.S | 10 ++ linkers/kernel.ld | 2 +- src/console.rs | 1 + src/constants.rs | 26 +++++- src/lapic.rs | 9 +- src/lib.rs | 30 ++++-- src/param.rs | 2 + src/proc.rs | 227 ++++++++++++++++++++++++++++++++++++++++++++-- src/spinlock.rs | 32 +++++++ src/vm.rs | 6 +- src/x86.rs | 78 ++++++++++++++++ swtch.S | 22 +++++ trapasm.S | 13 +++ 15 files changed, 443 insertions(+), 29 deletions(-) create mode 100644 initcode.S create mode 100644 src/spinlock.rs create mode 100644 swtch.S diff --git a/.gdbinit.tmpl b/.gdbinit.tmpl index 1dfd173..ca8444b 100644 --- a/.gdbinit.tmpl +++ b/.gdbinit.tmpl @@ -25,4 +25,4 @@ target remote localhost:1234 echo + symbol-file kernel\n symbol-file kernel -layout split \ No newline at end of file +# layout split \ No newline at end of file diff --git a/Makefile b/Makefile index c4a3ef4..90424f4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS = entry.o vectors.o trapasm.o +OBJS = entry.o vectors.o trapasm.o swtch.o RS = src/*.rs # Cross-compiling (e.g., on Mac OS X) @@ -70,6 +70,12 @@ bootblock: bootasm.S bootmain.c linkers/bootblock.ld $(OBJDUMP) -S bootblock.o > bootblock.asm $(OBJCOPY) -S -O binary bootblock.o bootblock +initcode: initcode.S + $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S + $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o + $(OBJCOPY) -S -O binary initcode.out initcode + $(OBJDUMP) -S initcode.o > initcode.asm + kernel.a: $(RS) cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ --target ./targets/i686.json --release @@ -79,8 +85,8 @@ kernel.a: $(RS) cp "$$lib" kernel.a -kernel: kernel.a $(OBJS) ./linkers/kernel.ld - $(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a +kernel: kernel.a $(OBJS) ./linkers/kernel.ld initcode + $(LD) -m elf_i386 -T ./linkers/kernel.ld -o kernel $(OBJS) kernel.a -b binary initcode $(OBJDUMP) -S -D kernel > kernel.asm $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym diff --git a/initcode.S b/initcode.S new file mode 100644 index 0000000..ea554dc --- /dev/null +++ b/initcode.S @@ -0,0 +1,10 @@ +# Initial process execs /init. +# This code runs in user space. + +# movl %eax, 0x10001 +# movl %eax, 0x1000100 +.globl start +start: + add $1, %eax + movl %eax, 0x10001 + jmp start diff --git a/linkers/kernel.ld b/linkers/kernel.ld index cf6630e..0b70493 100644 --- a/linkers/kernel.ld +++ b/linkers/kernel.ld @@ -8,7 +8,7 @@ ENTRY(_start) SECTIONS { /* Link the kernel at this address: "." means the current address */ - /* Must be equal to KERNLINK */ + /* Must be equal to KERNLINK */ . = 0x00100000; .text : AT(0x100000) { diff --git a/src/console.rs b/src/console.rs index 7ad6727..61d9302 100644 --- a/src/console.rs +++ b/src/console.rs @@ -76,6 +76,7 @@ pub fn consoleintr(getc: fn() -> i32) { (*input).e += 1; consputc(c); if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { + // call myproc with the buf (*input).w = (*input).e; } } diff --git a/src/constants.rs b/src/constants.rs index f7dd337..44dae04 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -15,15 +15,39 @@ pub const SEG_TSS: u16 = 5; // this process's task state // cpu->gdt[NSEGS] holds the above segments. pub const NSEGS: usize = 6; +// Privilege level +pub const DPL_USER: u8 = 0x3; // User DPL + // Application segment type bits pub const STA_X: u8 = 0x8; // Executable segment pub const STA_W: u8 = 0x2; // Writeable (non-executable segments) pub const STA_R: u8 = 0x2; // Readable (executable segments) // Memory layout +pub const EXTMEM: u32 = 0x100000; // Start of extended memory +pub const PHYSTOP: u32 = 0xE000000; // Top physical memory +pub const DEVSPACE: u32 = 0xFE000000; // Other devices are at high addresses + +// Key addresses for address space layout +pub const KERNBASE: u32 = 0x0; // First kernel virtual address +pub const KERNLINK: u32 = KERNBASE + EXTMEM; // Address where kernel is linked + // We assume that kernel.asm can fit in first 2MB pub const STARTPROC: u32 = 0x200000; // Start allocating process from here (2MB) -pub const PROCSIZE: u32 = 0x100000; // Size of each process (1MB) +pub const PROCSIZE: u32 = 0x100; // 1MB is the size of each process (in multiple of 4KB) + +// Page table constants +pub const PGSIZE: u32 = 4096; // bytes mapped by a page +pub const NPDENTRIES: usize = 1024; // # directory entries per page directory +pub const NPTENTRIES: usize = 1024; // # PTEs per page table +pub const PTXSHIFT: u32 = 12; // offset of PTX in a linear address +pub const PDXSHIFT: u32 = 22; // offset of PDX in a linear address + +// Page table/directory entry flags +pub const PTE_P: u32 = 0x001; // Present +pub const PTE_W: u32 = 0x002; // Writeable +pub const PTE_U: u32 = 0x004; // User +pub const PTE_PS: u32 = 0x080; // Page Size // System segment type bits pub const STS_T32A: u8 = 0x9; // Available 32-bit TSS diff --git a/src/lapic.rs b/src/lapic.rs index fc3dbca..5612dbb 100644 --- a/src/lapic.rs +++ b/src/lapic.rs @@ -1,6 +1,6 @@ use core::ptr::{read_volatile, write_volatile}; use crate::mp::MP_ONCE; -use crate::constants::{IRQ_ERROR, IRQ_SPURIOUS, IRQ_TIMER, T_IRQ0}; +use crate::constants::{IRQ_ERROR, IRQ_SPURIOUS, T_IRQ0}; const ID: isize = 0x0020 / 4; const VER: isize = 0x0030 / 4; @@ -68,9 +68,10 @@ pub fn lapicinit() { // from lapic[TICR] and then issues an interrupt. // If xv6 cared more about precise timekeeping, // TICR would be calibrated using an external time source. - lapicw(TDCR, X1); - lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 10000000); + // Timer disabled for p15 - will be re-enabled later + // lapicw(TDCR, X1); + // lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); + // lapicw(TICR, 10000000); // Disable logical interrupt lines. diff --git a/src/lib.rs b/src/lib.rs index a318bef..f66304b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ mod file; mod log; mod mmu; mod vm; +mod spinlock; use crate::traps::*; #[macro_export] @@ -102,22 +103,37 @@ pub extern "C" fn entryofrust() -> ! { log::initlog(param::ROOTDEV); file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); vm::seginit(); // segment descriptors - welcome(); - - loop { - x86::wfi(); - } + proc::pinit(); // first process + proc::scheduler(); // start running processes (never returns) } static mut PANICKED: bool = false; #[panic_handler] fn panic(info: &PanicInfo) -> ! { + use core::fmt::Write; + // Disable interrupts to prevent interrupt handlers from interfering cli(); + // Print panic message with LAPIC ID to identify which CPU panicked - println!("lapicid {}:\n{:#?}", lapicid(), info); + let mut console = console::Console {}; + let _ = write!(&mut console, "lapicid {}: panic: ", lapicid()); + let _ = writeln!(&mut console, "{}", info); + + // Print stack trace + let mut pcs = [0u32; 10]; + let stack_ptr = &info as *const _ as *const u32; + spinlock::getcallerpcs(stack_ptr, &mut pcs); + + for &pc in &pcs { + if pc != 0 { + let _ = writeln!(&mut console, " {:#x}", pc); + } + } + unsafe { PANICKED = true; } + // Halt the system - loop {} + halt(); } diff --git a/src/param.rs b/src/param.rs index 4a269c3..2b4ebc4 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,6 +1,8 @@ +pub const NPROC: usize = 64; // maximum number of processes pub const KSTACKSIZE: usize = 4096; // size of per-process kernel stack (unused in p3) pub const NCPU: usize = 8; // maximum number of CPUs pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes +pub const NOFILE: usize = 16; // open files per process pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes pub const NDEV: usize = 10; // maximum major device number diff --git a/src/proc.rs b/src/proc.rs index 44c0fad..460baed 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,29 +1,238 @@ -use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs -// use core::ptr; -use crate::constants::NSEGS; +use crate::mp::MP_ONCE; +use crate::constants::{NSEGS, SEG_UCODE, SEG_UDATA, DPL_USER, FL_IF, PGSIZE, STARTPROC, PROCSIZE}; use crate::mmu::SegDesc; +use crate::x86::TrapFrame; +use crate::param::NPROC; +use core::ptr::null_mut; +use core::cell::OnceCell; + +// Saved registers for kernel context switches. +// Don't need to save all the segment registers (%cs, etc), +// because they are constant across kernel contexts. +// Don't need to save %eax, %ecx, %edx, because the +// x86 convention is that the caller has saved them. +// Contexts are stored at the bottom of the stack they +// describe; the stack pointer is the address of the context. +// The layout of the context matches the layout of the stack in swtch.S +// at the "Switch stacks" comment. Switch doesn't save eip explicitly, +// but it is on the stack and allocproc() manipulates it. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct Context { + pub edi: u32, + pub esi: u32, + pub ebx: u32, + pub ebp: u32, + pub eip: u32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcState { + Unused, + Embryo, + Runnable, + Running, +} + +// Per-process state +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Proc { + pub kstack: *mut u8, // Bottom of kernel stack for this process (unused for now) + pub state: ProcState, // Process state + pub pid: i32, // Process ID + pub parent: *mut Proc, // Parent process + pub tf: *mut TrapFrame, // Trap frame for current syscall + pub context: *mut Context, // swtch() here to run process + pub cwd: usize, // Current directory (inode number) + pub name: [u8; 16], // Process name (debugging) +} + +impl Proc { + pub const fn new() -> Self { + Self { + kstack: null_mut(), + state: ProcState::Unused, + pid: 0, + parent: null_mut(), + tf: null_mut(), + context: null_mut(), + cwd: 0, + name: [0; 16], + } + } +} + +// Process table +struct PTable { + proc: [Proc; NPROC], +} + +static mut PTABLE: OnceCell = OnceCell::new(); +static mut NEXTPID: i32 = 1; #[derive(Debug, Clone, Copy)] pub struct Cpu { - pub apicid: u8, // Local APIC ID - pub gdt: [SegDesc; NSEGS], // x86 global descriptor table + pub apicid: u8, // Local APIC ID + pub scheduler: *mut Context, // swtch() here to enter scheduler + pub gdt: [SegDesc; NSEGS], // x86 global descriptor table + pub proc: *mut Proc, // The process running on this cpu or null } impl Cpu { pub const fn new() -> Self { Self { apicid: 0, + scheduler: null_mut(), gdt: [SegDesc::new(); NSEGS], + proc: null_mut(), } } } pub fn cpuid() -> usize { - let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); - unsafe { (mycpu() as *const Cpu).offset_from(cpus.as_ptr()) as usize } + // For now, always return 0 (single CPU) + 0 } -pub fn mycpu() -> &'static Cpu { +pub fn mycpu() -> &'static mut Cpu { let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); - &cpus[0] + unsafe { &mut *(cpus.as_ptr() as *mut Cpu).add(cpuid()) } +} + +// Read proc from the cpu structure +pub fn myproc() -> Option<&'static mut Proc> { + let c = mycpu(); + if c.proc.is_null() { + None + } else { + Some(unsafe { &mut *c.proc }) + } +} + +// External symbols from assembly +extern "C" { + fn trapret(); + pub fn swtch(context: *mut Context); +} + +// Look in the process table for an UNUSED proc. +// If found, change state to EMBRYO and initialize +// state required to run in the kernel. +// Otherwise return None. +fn allocproc() -> Option<&'static mut Proc> { + unsafe { + let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); + + if (*ptable_ptr).get().is_none() { + let _ = (*ptable_ptr).set(PTable { + proc: [Proc::new(); NPROC], + }); + } + + let ptable = (*ptable_ptr).get_mut().unwrap(); + + for p in &mut ptable.proc { + if p.state == ProcState::Unused { + // Found an unused process + p.state = ProcState::Embryo; + p.pid = NEXTPID; + NEXTPID += 1; + + // Calculate stack pointer at the end of process memory + let sp = (STARTPROC + (PROCSIZE << 12)) as *mut u8; + + // Leave room for trap frame + let sp = sp.sub(core::mem::size_of::()); + p.tf = sp as *mut TrapFrame; + + // Leave room for context + let sp = sp.sub(core::mem::size_of::()); + p.context = sp as *mut Context; + + // Initialize context + core::ptr::write_bytes(p.context, 0, 1); + (*p.context).eip = trapret as *const () as usize as u32; + + return Some(p); + } + } + + None + } +} + +// Set up first process. +pub fn pinit() { + unsafe { + extern "C" { + static _binary_initcode_start: u8; + static _binary_initcode_size: usize; + } + + let p = allocproc().expect("Failed to allocate first process"); + + // Copy initcode binary to STARTPROC + let dst = STARTPROC as *mut u8; + let src = &_binary_initcode_start as *const u8; + let size = &_binary_initcode_size as *const usize as usize; + core::ptr::copy_nonoverlapping(src, dst, size); + + // Initialize trapframe + core::ptr::write_bytes(p.tf, 0, 1); + + (*p.tf).cs = ((SEG_UCODE << 3) | DPL_USER as u16) as u16; + (*p.tf).ds = ((SEG_UDATA << 3) | DPL_USER as u16) as u16; + (*p.tf).es = (*p.tf).ds; + (*p.tf).ss = (*p.tf).ds; + (*p.tf).eflags = FL_IF; + (*p.tf).esp = PGSIZE; + (*p.tf).eip = 0; // beginning of initcode.S + + // Set process name + let name = b"initcode"; + for (i, &byte) in name.iter().enumerate() { + p.name[i] = byte; + } + + // Set current working directory to root + p.cwd = crate::fs::namei("/").expect("Failed to find root directory"); + + p.state = ProcState::Runnable; + } +} + +// Process scheduler. +// Scheduler never returns. It loops, doing: +// - choose a process to run +// - swtch to start running that process +pub fn scheduler() -> ! { + let c = mycpu(); + c.proc = null_mut(); + + loop { + // Enable interrupts on this processor. + crate::x86::sti(); + + // Loop over process table looking for process to run. + unsafe { + let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); + let ptable = (*ptable_ptr).get_mut().expect("Process table not initialized"); + + for p in &mut ptable.proc { + if p.state != ProcState::Runnable { + continue; + } + + // Switch to chosen process. + c.proc = p as *mut Proc; + p.state = ProcState::Running; + + swtch(p.context); + + // Process is done running for now. + c.proc = null_mut(); + } + } + } } \ No newline at end of file diff --git a/src/spinlock.rs b/src/spinlock.rs new file mode 100644 index 0000000..2e7b2f2 --- /dev/null +++ b/src/spinlock.rs @@ -0,0 +1,32 @@ +// Mutual exclusion spin locks and debugging utilities + +/// Record the current call stack in pcs[] by following the %ebp chain. +/// This function walks the stack frame pointers to collect return addresses. +pub fn getcallerpcs(v: *const u32, pcs: &mut [u32; 10]) { + unsafe { + let mut ebp = v.offset(-2) as *const u32; + let mut i = 0; + + while i < 10 { + // Check for invalid ebp values + // if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff) + if ebp.is_null() || ebp == 0xffffffff as *const u32 { + break; + } + + // pcs[i] = ebp[1]; // saved %eip + pcs[i] = *ebp.offset(1); + + // ebp = (uint*)ebp[0]; // saved %ebp + ebp = *ebp as *const u32; + + i += 1; + } + + // Fill rest with zeros + while i < 10 { + pcs[i] = 0; + i += 1; + } + } +} diff --git a/src/vm.rs b/src/vm.rs index 20baffb..7d6984f 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,4 +1,4 @@ -use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE}; +use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE, DPL_USER}; use crate::mmu::SegDesc; use crate::proc::cpuid; use crate::mp::MP_ONCE; @@ -16,8 +16,8 @@ pub fn seginit() { c.gdt[SEG_KCODE as usize] = SegDesc::seg(STA_X | STA_R, 0, 0xffffffff, 0); c.gdt[SEG_KDATA as usize] = SegDesc::seg(STA_W, 0, 0xffffffff, 0); - c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, PROCSIZE, 0); - c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, PROCSIZE, 0); + c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, PROCSIZE << 12, DPL_USER); + c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, PROCSIZE << 12, DPL_USER); lgdt(&c.gdt, size_of_val(&c.gdt)); } } diff --git a/src/x86.rs b/src/x86.rs index 248683d..48187b0 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -139,6 +139,42 @@ pub fn rcr2() -> u32 { } } +pub fn stosb(addr: *mut u8, data: u8, cnt: usize) { + unsafe { + asm!( + "cld", + "rep stosb", + inout("edi") addr => _, + inout("ecx") cnt => _, + in("al") data, + options(nostack) + ); + } +} + +pub fn stosl(addr: *mut u32, data: u32, cnt: usize) { + unsafe { + asm!( + "cld", + "rep stosl", + inout("edi") addr => _, + inout("ecx") cnt => _, + in("eax") data, + options(nostack) + ); + } +} + +pub fn loadgs(v: u16) { + unsafe { + asm!( + "mov gs, {0:x}", + in(reg) v, + options(nomem, nostack) + ); + } +} + #[repr(C)] pub struct TrapFrame { // registers as pushed by pusha @@ -151,6 +187,15 @@ pub struct TrapFrame { pub ecx: u32, pub eax: u32, + // segment registers + pub gs: u16, + pub padding1: u16, + pub fs: u16, + pub padding2: u16, + pub es: u16, + pub padding3: u16, + pub ds: u16, + pub padding4: u16, pub trapno: u32, // below here defined by x86 hardware @@ -164,4 +209,37 @@ pub struct TrapFrame { pub esp: u32, pub ss: u16, pub padding6: u16, +} + +// Page table/directory helper functions +use crate::constants::{PDXSHIFT, PTXSHIFT}; + +// Extract page directory index from virtual address +#[inline] +pub fn pdx(va: u32) -> usize { + ((va >> PDXSHIFT) & 0x3FF) as usize +} + +// Extract page table index from virtual address +#[inline] +pub fn ptx(va: u32) -> usize { + ((va >> PTXSHIFT) & 0x3FF) as usize +} + +// Construct virtual address from page directory index, page table index, and offset +#[inline] +pub fn pgaddr(d: u32, t: u32, o: u32) -> u32 { + (d << PDXSHIFT) | (t << PTXSHIFT) | o +} + +// Extract address from page table entry +#[inline] +pub fn pte_addr(pte: u32) -> u32 { + pte & !0xFFF +} + +// Extract flags from page table entry +#[inline] +pub fn pte_flags(pte: u32) -> u32 { + pte & 0xFFF } \ No newline at end of file diff --git a/swtch.S b/swtch.S new file mode 100644 index 0000000..60479ec --- /dev/null +++ b/swtch.S @@ -0,0 +1,22 @@ +# Context switch +# +# void swtch(struct context *new); +# +# Save the current registers on the stack, creating +# a struct context, and save its address in *old. +# Switch stacks to new and pop previously-saved registers. + +.globl swtch +swtch: + movl 4(%esp), %eax + + # Switch stacks + movl %eax, %esp + movl $0, %eax + + # Load new callee-saved registers + popl %edi + popl %esi + popl %ebx + popl %ebp + ret diff --git a/trapasm.S b/trapasm.S index 477a5ee..7f46ce1 100644 --- a/trapasm.S +++ b/trapasm.S @@ -4,8 +4,17 @@ .globl alltraps alltraps: # Build trap frame. + pushl %ds + pushl %es + pushl %fs + pushl %gs pushal + # Set up data segments. + movw $(SEG_KDATA<<3), %ax + movw %ax, %ds + movw %ax, %es + # Call trap(tf), where tf=%esp pushl %esp call trap @@ -15,6 +24,10 @@ alltraps: .globl trapret trapret: popal + popl %gs + popl %fs + popl %es + popl %ds addl $0x8, %esp # trapno and errcode iret From 771466f5cdb09d60a57402595502b860d0c36844 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Fri, 6 Mar 2026 00:25:52 +0530 Subject: [PATCH 65/69] p16-tss: add TSS switch path and process dump --- Makefile | 2 +- src/console.rs | 8 ++++ src/mmu.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++++ src/proc.rs | 38 +++++++++++++++-- src/spinlock.rs | 29 +++++++++++++ src/vm.rs | 41 +++++++++++++++--- src/x86.rs | 16 ++++++- targets/i686.json | 6 +-- 8 files changed, 232 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 90424f4..d63f252 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ initcode: initcode.S $(OBJDUMP) -S initcode.o > initcode.asm kernel.a: $(RS) - cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec \ + cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem \ --target ./targets/i686.json --release @tdir=$$(cargo metadata --format-version=1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p'); \ lib=$$(find "$$tdir" -maxdepth 4 -type f -name 'libkernel.a' | head -n 1); \ diff --git a/src/console.rs b/src/console.rs index 61d9302..c97c7a0 100644 --- a/src/console.rs +++ b/src/console.rs @@ -48,6 +48,7 @@ pub fn consputc(c: i32) { } pub fn consoleintr(getc: fn() -> i32) { + let mut doprocdump = false; loop { let c = getc(); if c < 0 { @@ -57,6 +58,10 @@ pub fn consoleintr(getc: fn() -> i32) { unsafe { let input = &raw mut INPUT; match c { + x if x == C('P') => { + // procdump() may indirectly use console output; call after loop + doprocdump = true; + } x if x == C('U') => { while (*input).e != (*input).w && (*input).buf[((*input).e - 1) % INPUT_BUF] != b'\n' { (*input).e -= 1; @@ -84,6 +89,9 @@ pub fn consoleintr(getc: fn() -> i32) { } } } + if doprocdump { + crate::proc::procdump(); + } } pub fn consoleread(_ip: usize, dst: &mut [u8], n: i32) -> i32 { diff --git a/src/mmu.rs b/src/mmu.rs index 505b33a..861dc0f 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -43,4 +43,110 @@ impl SegDesc { seg.set_base_31_24(((base >> 24) & 0xff) as u8); seg } + + /// Create a 16-bit system segment descriptor. + /// Matches the C macro: SEG16(type, base, lim, dpl) + pub fn seg16(seg_type: u8, base: u32, lim: u32, dpl: u8) -> Self { + let mut seg = SegDesc::default(); + seg.set_lim_15_0((lim & 0xffff) as u16); + seg.set_base_15_0((base & 0xffff) as u16); + seg.set_base_23_16(((base >> 16) & 0xff) as u8); + seg.set_seg_type(seg_type); + seg.set_s(0); + seg.set_dpl(dpl); + seg.set_p(1); + seg.set_lim_19_16(((lim >> 16) & 0xf) as u8); + seg.set_avl(0); + seg.set_rsv1(0); + seg.set_db(0); + seg.set_g(0); + seg.set_base_31_24(((base >> 24) & 0xff) as u8); + seg + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TaskState { + pub link: u32, + pub esp0: u32, + pub ss0: u16, + pub padding1: u16, + pub esp1: u32, + pub ss1: u16, + pub padding2: u16, + pub esp2: u32, + pub ss2: u16, + pub padding3: u16, + pub cr3: u32, + pub eip: u32, + pub eflags: u32, + pub eax: u32, + pub ecx: u32, + pub edx: u32, + pub ebx: u32, + pub esp: u32, + pub ebp: u32, + pub esi: u32, + pub edi: u32, + pub es: u16, + pub padding4: u16, + pub cs: u16, + pub padding5: u16, + pub ss: u16, + pub padding6: u16, + pub ds: u16, + pub padding7: u16, + pub fs: u16, + pub padding8: u16, + pub gs: u16, + pub padding9: u16, + pub ldt: u16, + pub padding10: u16, + pub t: u16, + pub iomb: u16, +} + +impl TaskState { + pub const fn new() -> Self { + Self { + link: 0, + esp0: 0, + ss0: 0, + padding1: 0, + esp1: 0, + ss1: 0, + padding2: 0, + esp2: 0, + ss2: 0, + padding3: 0, + cr3: 0, + eip: 0, + eflags: 0, + eax: 0, + ecx: 0, + edx: 0, + ebx: 0, + esp: 0, + ebp: 0, + esi: 0, + edi: 0, + es: 0, + padding4: 0, + cs: 0, + padding5: 0, + ss: 0, + padding6: 0, + ds: 0, + padding7: 0, + fs: 0, + padding8: 0, + gs: 0, + padding9: 0, + ldt: 0, + padding10: 0, + t: 0, + iomb: 0, + } + } } diff --git a/src/proc.rs b/src/proc.rs index 460baed..291fde6 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,7 +1,8 @@ use crate::mp::MP_ONCE; use crate::constants::{NSEGS, SEG_UCODE, SEG_UDATA, DPL_USER, FL_IF, PGSIZE, STARTPROC, PROCSIZE}; -use crate::mmu::SegDesc; +use crate::mmu::{SegDesc, TaskState}; use crate::x86::TrapFrame; +use crate::param::KSTACKSIZE; use crate::param::NPROC; use core::ptr::null_mut; use core::cell::OnceCell; @@ -75,7 +76,10 @@ static mut NEXTPID: i32 = 1; pub struct Cpu { pub apicid: u8, // Local APIC ID pub scheduler: *mut Context, // swtch() here to enter scheduler + pub ts: TaskState, // Used by x86 to find stack for interrupt pub gdt: [SegDesc; NSEGS], // x86 global descriptor table + pub ncli: i32, // Depth of pushcli nesting. + pub intena: bool, // Were interrupts enabled before pushcli? pub proc: *mut Proc, // The process running on this cpu or null } @@ -84,7 +88,10 @@ impl Cpu { Self { apicid: 0, scheduler: null_mut(), + ts: TaskState::new(), gdt: [SegDesc::new(); NSEGS], + ncli: 0, + intena: false, proc: null_mut(), } } @@ -141,6 +148,7 @@ fn allocproc() -> Option<&'static mut Proc> { // Calculate stack pointer at the end of process memory let sp = (STARTPROC + (PROCSIZE << 12)) as *mut u8; + p.kstack = sp.sub(KSTACKSIZE); // Leave room for trap frame let sp = sp.sub(core::mem::size_of::()); @@ -227,7 +235,7 @@ pub fn scheduler() -> ! { // Switch to chosen process. c.proc = p as *mut Proc; p.state = ProcState::Running; - + crate::vm::switchuvm(c.proc); swtch(p.context); // Process is done running for now. @@ -235,4 +243,28 @@ pub fn scheduler() -> ! { } } } -} \ No newline at end of file +} + +pub fn procdump() { + unsafe { + let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); + if (*ptable_ptr).get().is_none() { + return; + } + let ptable = (*ptable_ptr).get().unwrap(); + for p in &ptable.proc { + if p.state == ProcState::Unused { + continue; + } + let state = match p.state { + ProcState::Unused => "unused", + ProcState::Embryo => "embryo", + ProcState::Runnable => "runble", + ProcState::Running => "run ", + }; + let name_len = p.name.iter().position(|&b| b == 0).unwrap_or(p.name.len()); + let name = core::str::from_utf8(&p.name[..name_len]).unwrap_or("???"); + crate::println!("{} {} {}", p.pid, state, name); + } + } +} diff --git a/src/spinlock.rs b/src/spinlock.rs index 2e7b2f2..ca47949 100644 --- a/src/spinlock.rs +++ b/src/spinlock.rs @@ -1,4 +1,7 @@ // Mutual exclusion spin locks and debugging utilities +use crate::constants::FL_IF; +use crate::proc::mycpu; +use crate::x86::{cli, readeflags, sti}; /// Record the current call stack in pcs[] by following the %ebp chain. /// This function walks the stack frame pointers to collect return addresses. @@ -30,3 +33,29 @@ pub fn getcallerpcs(v: *const u32, pcs: &mut [u32; 10]) { } } } + +// Pushcli/popcli are like cli/sti except that they are matched: +// it takes two popcli to undo two pushcli. +pub fn pushcli() { + let eflags = readeflags(); + cli(); + let c = mycpu(); + if c.ncli == 0 { + c.intena = (eflags & FL_IF) != 0; + } + c.ncli += 1; +} + +pub fn popcli() { + if (readeflags() & FL_IF) != 0 { + panic!("popcli - interruptible"); + } + let c = mycpu(); + c.ncli -= 1; + if c.ncli < 0 { + panic!("popcli"); + } + if c.ncli == 0 && c.intena { + sti(); + } +} diff --git a/src/vm.rs b/src/vm.rs index 7d6984f..b593d3a 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,9 +1,11 @@ -use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE, DPL_USER}; +use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, SEG_TSS, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE, DPL_USER, STS_T32A}; use crate::mmu::SegDesc; -use crate::proc::cpuid; +use crate::proc::{cpuid, mycpu, Proc}; use crate::mp::MP_ONCE; -use crate::x86::lgdt; -use core::mem::size_of_val; +use crate::x86::{lgdt, ltr}; +use core::mem::{size_of, size_of_val}; +use crate::param::KSTACKSIZE; +use crate::spinlock::{pushcli, popcli}; /// Set up CPU's kernel segment descriptors. /// Run once on entry on each CPU. @@ -16,8 +18,35 @@ pub fn seginit() { c.gdt[SEG_KCODE as usize] = SegDesc::seg(STA_X | STA_R, 0, 0xffffffff, 0); c.gdt[SEG_KDATA as usize] = SegDesc::seg(STA_W, 0, 0xffffffff, 0); - c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, PROCSIZE << 12, DPL_USER); - c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, PROCSIZE << 12, DPL_USER); + c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, (PROCSIZE << 12) - 1, DPL_USER); + c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, (PROCSIZE << 12) - 1, DPL_USER); lgdt(&c.gdt, size_of_val(&c.gdt)); } } + +pub fn switchuvm(p: *mut Proc) { + if p.is_null() { + panic!("switchuvm: no process"); + } + unsafe { + if (*p).kstack.is_null() { + panic!("switchuvm: no kstack"); + } + } + + pushcli(); + unsafe { + let c = mycpu(); + c.gdt[SEG_TSS as usize] = SegDesc::seg16( + STS_T32A, + (&c.ts as *const _ as usize) as u32, + (size_of::() - 1) as u32, + 0, + ); + c.ts.ss0 = SEG_KDATA << 3; + c.ts.esp0 = ((*p).kstack as usize + KSTACKSIZE) as u32; + c.ts.iomb = 0xFFFF; + ltr(SEG_TSS << 3); + } + popcli(); +} diff --git a/src/x86.rs b/src/x86.rs index 48187b0..7713760 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -81,6 +81,20 @@ pub fn lgdt(gdt: *const [SegDesc; NSEGS], size: usize) { } } +pub fn ltr(sel: u16) { + unsafe { + asm!("ltr {0:x}", in(reg) sel, options(nomem, nostack)); + } +} + +pub fn readeflags() -> u32 { + unsafe { + let eflags: u32; + asm!("pushfd; pop eax", out("eax") eflags, options(nomem, nostack)); + eflags + } +} + pub fn noop() { core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); } @@ -242,4 +256,4 @@ pub fn pte_addr(pte: u32) -> u32 { #[inline] pub fn pte_flags(pte: u32) -> u32 { pte & 0xFFF -} \ No newline at end of file +} diff --git a/targets/i686.json b/targets/i686.json index 4cd8fa9..2f45047 100644 --- a/targets/i686.json +++ b/targets/i686.json @@ -10,7 +10,7 @@ "llvm-target": "i686-unknown-none", "os": "none", "panic-strategy": "abort", - "target-c-int-width": 32, + "target-c-int-width": "32", "target-endian": "little", - "target-pointer-width": 32 -} \ No newline at end of file + "target-pointer-width": "32" +} From a126cb5b12d93752ff49a45da8b3fe6230e9f030 Mon Sep 17 00:00:00 2001 From: Amber-Agarwal Date: Fri, 6 Mar 2026 00:35:24 +0530 Subject: [PATCH 66/69] p17-free-space: add freelist allocator and per-proc user segments --- src/constants.rs | 10 ++++++++-- src/kalloc.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++++++++ src/proc.rs | 17 ++++++++++++---- src/vm.rs | 10 +++++++--- 5 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 src/kalloc.rs diff --git a/src/constants.rs b/src/constants.rs index 44dae04..75a41df 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -25,6 +25,7 @@ pub const STA_R: u8 = 0x2; // Readable (executable segments) // Memory layout pub const EXTMEM: u32 = 0x100000; // Start of extended memory +pub const PHYSTART: u32 = EXTMEM + PROCSIZE; pub const PHYSTOP: u32 = 0xE000000; // Top physical memory pub const DEVSPACE: u32 = 0xFE000000; // Other devices are at high addresses @@ -37,12 +38,17 @@ pub const STARTPROC: u32 = 0x200000; // Start allocating process from here (2MB pub const PROCSIZE: u32 = 0x100; // 1MB is the size of each process (in multiple of 4KB) // Page table constants -pub const PGSIZE: u32 = 4096; // bytes mapped by a page +pub const PGSIZE: u32 = PROCSIZE << 12; // Process allocation granularity in bytes pub const NPDENTRIES: usize = 1024; // # directory entries per page directory pub const NPTENTRIES: usize = 1024; // # PTEs per page table pub const PTXSHIFT: u32 = 12; // offset of PTX in a linear address pub const PDXSHIFT: u32 = 22; // offset of PDX in a linear address +#[inline] +pub const fn pgroundup(sz: usize) -> usize { + (sz + PGSIZE as usize - 1) & !(PGSIZE as usize - 1) +} + // Page table/directory entry flags pub const PTE_P: u32 = 0x001; // Present pub const PTE_W: u32 = 0x002; // Writeable @@ -185,4 +191,4 @@ pub const NINODES: u32 = 200; // number of inodes in file system pub const T_DIR: u16 = 1; // Directory pub const T_FILE: u16 = 2; // File -pub const T_DEV: u16 = 3; // Device \ No newline at end of file +pub const T_DEV: u16 = 3; // Device diff --git a/src/kalloc.rs b/src/kalloc.rs new file mode 100644 index 0000000..6e65192 --- /dev/null +++ b/src/kalloc.rs @@ -0,0 +1,52 @@ +use crate::constants::{pgroundup, PGSIZE}; +use core::ptr::null_mut; + +#[repr(C)] +struct Run { + next: *mut Run, +} + +struct KMem { + freelist: *mut Run, +} + +static mut KMEM: KMem = KMem { freelist: null_mut() }; + +extern "C" { + static end: u8; +} + +pub fn kinit(vstart: *mut u8, vend: *mut u8) { + freerange(vstart, vend); +} + +fn freerange(vstart: *mut u8, vend: *mut u8) { + let mut p = pgroundup(vstart as usize) as *mut u8; + while (p as usize) + PGSIZE as usize <= vend as usize { + kfree(p); + p = unsafe { p.add(PGSIZE as usize) }; + } +} + +pub fn kfree(v: *mut u8) { + if (v as usize) % PGSIZE as usize != 0 || (v as usize) < (unsafe { &end as *const u8 as usize }) { + panic!("kfree"); + } + + unsafe { + core::ptr::write_bytes(v, 1, PGSIZE as usize); + let r = v as *mut Run; + (*r).next = KMEM.freelist; + KMEM.freelist = r; + } +} + +pub fn kalloc() -> *mut u8 { + unsafe { + let r = KMEM.freelist; + if !r.is_null() { + KMEM.freelist = (*r).next; + } + r as *mut u8 + } +} diff --git a/src/lib.rs b/src/lib.rs index f66304b..345dd53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,9 @@ mod log; mod mmu; mod vm; mod spinlock; +mod kalloc; use crate::traps::*; +use crate::constants::PHYSTOP; #[macro_export] macro_rules! println { @@ -88,6 +90,11 @@ extern "C" { #[no_mangle] pub extern "C" fn entryofrust() -> ! { + extern "C" { + static end: u8; + } + + kalloc::kinit(unsafe { &end as *const u8 as *mut u8 }, PHYSTOP as *mut u8); mp::mpinit(); lapic::lapicinit(); picirq::picinit(); @@ -104,6 +111,7 @@ pub extern "C" fn entryofrust() -> ! { file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); vm::seginit(); // segment descriptors proc::pinit(); // first process + proc::pinit(); // another process proc::scheduler(); // start running processes (never returns) } diff --git a/src/proc.rs b/src/proc.rs index 291fde6..9f2a7c3 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,9 +1,10 @@ use crate::mp::MP_ONCE; -use crate::constants::{NSEGS, SEG_UCODE, SEG_UDATA, DPL_USER, FL_IF, PGSIZE, STARTPROC, PROCSIZE}; +use crate::constants::{NSEGS, SEG_UCODE, SEG_UDATA, DPL_USER, FL_IF, PGSIZE}; use crate::mmu::{SegDesc, TaskState}; use crate::x86::TrapFrame; use crate::param::KSTACKSIZE; use crate::param::NPROC; +use crate::kalloc::kalloc; use core::ptr::null_mut; use core::cell::OnceCell; @@ -39,6 +40,7 @@ pub enum ProcState { #[repr(C)] #[derive(Clone, Copy)] pub struct Proc { + pub offset: *mut u8, // Process memory base pub kstack: *mut u8, // Bottom of kernel stack for this process (unused for now) pub state: ProcState, // Process state pub pid: i32, // Process ID @@ -52,6 +54,7 @@ pub struct Proc { impl Proc { pub const fn new() -> Self { Self { + offset: null_mut(), kstack: null_mut(), state: ProcState::Unused, pid: 0, @@ -145,9 +148,15 @@ fn allocproc() -> Option<&'static mut Proc> { p.state = ProcState::Embryo; p.pid = NEXTPID; NEXTPID += 1; + + p.offset = kalloc(); + if p.offset.is_null() { + p.state = ProcState::Unused; + return None; + } // Calculate stack pointer at the end of process memory - let sp = (STARTPROC + (PROCSIZE << 12)) as *mut u8; + let sp = p.offset.add(PGSIZE as usize); p.kstack = sp.sub(KSTACKSIZE); // Leave room for trap frame @@ -180,8 +189,8 @@ pub fn pinit() { let p = allocproc().expect("Failed to allocate first process"); - // Copy initcode binary to STARTPROC - let dst = STARTPROC as *mut u8; + // Copy initcode binary to process memory + let dst = p.offset; let src = &_binary_initcode_start as *const u8; let size = &_binary_initcode_size as *const usize as usize; core::ptr::copy_nonoverlapping(src, dst, size); diff --git a/src/vm.rs b/src/vm.rs index b593d3a..01d125e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,4 +1,4 @@ -use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, SEG_TSS, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE, DPL_USER, STS_T32A}; +use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, SEG_TSS, STA_X, STA_W, STA_R, PROCSIZE, DPL_USER, STS_T32A}; use crate::mmu::SegDesc; use crate::proc::{cpuid, mycpu, Proc}; use crate::mp::MP_ONCE; @@ -18,8 +18,6 @@ pub fn seginit() { c.gdt[SEG_KCODE as usize] = SegDesc::seg(STA_X | STA_R, 0, 0xffffffff, 0); c.gdt[SEG_KDATA as usize] = SegDesc::seg(STA_W, 0, 0xffffffff, 0); - c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, (PROCSIZE << 12) - 1, DPL_USER); - c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, (PROCSIZE << 12) - 1, DPL_USER); lgdt(&c.gdt, size_of_val(&c.gdt)); } } @@ -37,6 +35,12 @@ pub fn switchuvm(p: *mut Proc) { pushcli(); unsafe { let c = mycpu(); + c.gdt[SEG_UCODE as usize] = + SegDesc::seg(STA_X | STA_R, (*p).offset as u32, (PROCSIZE << 12) - 1, DPL_USER); + c.gdt[SEG_UDATA as usize] = + SegDesc::seg(STA_W, (*p).offset as u32, (PROCSIZE << 12) - 1, DPL_USER); + lgdt(&c.gdt, size_of_val(&c.gdt)); + c.gdt[SEG_TSS as usize] = SegDesc::seg16( STS_T32A, (&c.ts as *const _ as usize) as u32, From f6ddc426a623dc3bc461ade5aa80321424dd50db Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sat, 7 Mar 2026 08:33:54 +0000 Subject: [PATCH 67/69] Disabled LTO --- Cargo.toml | 2 +- Makefile | 2 +- targets/i686.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c2171c..ccd260b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,4 @@ modular-bitfield = "0.11.2" panic="abort" opt-level = "s" # Optimize for size codegen-units = 1 # Fewer units for better cross-function inlining -lto = true # Link-time optimization \ No newline at end of file +lto = false # Disable Link-time optimization \ No newline at end of file diff --git a/Makefile b/Makefile index d63f252..fd3dcae 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ initcode: initcode.S $(OBJDUMP) -S initcode.o > initcode.asm kernel.a: $(RS) - cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem \ + cargo build -Z build-std=core -Z build-std-features=compiler-builtins-mem -Z json-target-spec\ --target ./targets/i686.json --release @tdir=$$(cargo metadata --format-version=1 --no-deps | sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p'); \ lib=$$(find "$$tdir" -maxdepth 4 -type f -name 'libkernel.a' | head -n 1); \ diff --git a/targets/i686.json b/targets/i686.json index 2f45047..f72fa9a 100644 --- a/targets/i686.json +++ b/targets/i686.json @@ -10,7 +10,7 @@ "llvm-target": "i686-unknown-none", "os": "none", "panic-strategy": "abort", - "target-c-int-width": "32", + "target-c-int-width": 32, "target-endian": "little", - "target-pointer-width": "32" + "target-pointer-width": 32 } From 7bf8e2c557f7009f0f4bd584e54426bdac11f0fd Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Sat, 14 Mar 2026 18:44:28 +0000 Subject: [PATCH 68/69] Fixed proc.rs according p19 --- src/proc.rs | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/proc.rs b/src/proc.rs index 9f2a7c3..0be5263 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -72,7 +72,9 @@ struct PTable { proc: [Proc; NPROC], } -static mut PTABLE: OnceCell = OnceCell::new(); +static mut PTABLE: PTable = PTable { + proc: [Proc::new(); NPROC], +}; static mut NEXTPID: i32 = 1; #[derive(Debug, Clone, Copy)] @@ -132,17 +134,11 @@ extern "C" { // Otherwise return None. fn allocproc() -> Option<&'static mut Proc> { unsafe { - let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); - if (*ptable_ptr).get().is_none() { - let _ = (*ptable_ptr).set(PTable { - proc: [Proc::new(); NPROC], - }); - } - let ptable = (*ptable_ptr).get_mut().unwrap(); + let ptable = &raw mut PTABLE; - for p in &mut ptable.proc { + for p in &mut (*ptable).proc { if p.state == ProcState::Unused { // Found an unused process p.state = ProcState::Embryo; @@ -184,7 +180,7 @@ pub fn pinit() { unsafe { extern "C" { static _binary_initcode_start: u8; - static _binary_initcode_size: usize; + static _binary_initcode_size: u8; } let p = allocproc().expect("Failed to allocate first process"); @@ -192,7 +188,7 @@ pub fn pinit() { // Copy initcode binary to process memory let dst = p.offset; let src = &_binary_initcode_start as *const u8; - let size = &_binary_initcode_size as *const usize as usize; + let size = &_binary_initcode_size as *const u8 as usize; core::ptr::copy_nonoverlapping(src, dst, size); // Initialize trapframe @@ -233,10 +229,9 @@ pub fn scheduler() -> ! { // Loop over process table looking for process to run. unsafe { - let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); - let ptable = (*ptable_ptr).get_mut().expect("Process table not initialized"); + let ptable = &raw mut PTABLE; - for p in &mut ptable.proc { + for p in &mut (*ptable).proc { if p.state != ProcState::Runnable { continue; } @@ -256,12 +251,8 @@ pub fn scheduler() -> ! { pub fn procdump() { unsafe { - let ptable_ptr = core::ptr::addr_of_mut!(PTABLE); - if (*ptable_ptr).get().is_none() { - return; - } - let ptable = (*ptable_ptr).get().unwrap(); - for p in &ptable.proc { + let ptable = &raw mut PTABLE; + for p in &(*ptable).proc { if p.state == ProcState::Unused { continue; } From 2d7e77fc92b57b1414384ba751c007a2b4ec5edd Mon Sep 17 00:00:00 2001 From: Yash Rawat Date: Sun, 15 Mar 2026 00:21:38 +0530 Subject: [PATCH 69/69] Enable Link-time optimization in release profile --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ccd260b..a5f3964 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,4 @@ modular-bitfield = "0.11.2" panic="abort" opt-level = "s" # Optimize for size codegen-units = 1 # Fewer units for better cross-function inlining -lto = false # Disable Link-time optimization \ No newline at end of file +lto = true # Enabled Link-time optimization