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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)"; \
Expand All @@ -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)
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
130 changes: 130 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
@@ -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)
148 changes: 148 additions & 0 deletions src/bio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use core::sync::atomic::Ordering;

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

const HEAD: usize = NBUF; // sentinel index

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

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

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

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

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

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

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

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

BCACHE.head_next = i;
}
}

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

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

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

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

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

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

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

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

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

idx
}

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

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

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

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

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

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

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

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

pub data: [u8; BSIZE],
}

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