diff --git a/.gitmodules b/.gitmodules index 2d6ca04..f7f04bf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,15 @@ [submodule "programs/userutils"] path = programs/userutils url = https://github.com/redox-os/userutils.git +[submodule "programs/netutils"] + path = programs/netutils + url = https://github.com/redox-os/netutils.git +[submodule "schemes/orbital"] + path = schemes/orbital + url = https://github.com/redox-os/orbital.git +[submodule "programs/orbutils"] + path = programs/orbutils + url = https://github.com/redox-os/orbutils.git +[submodule "filesystem/ui"] + path = filesystem/ui + url = https://github.com/redox-os/orbdata.git diff --git a/Makefile b/Makefile index a9e71b3..d4c9269 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ CARGO=RUSTC="$(RUSTC)" cargo CARGOFLAGS=--target $(TARGET).json -- -C opt-level=s --cfg redox # Default targets -.PHONY: all clean qemu bochs FORCE +.PHONY: all clean qemu bochs drivers schemes coreutils extrautils netutils userutils wireshark FORCE all: $(KBUILD)/harddrive.bin @@ -25,6 +25,7 @@ clean: cargo clean cargo clean --manifest-path libstd/Cargo.toml cargo clean --manifest-path drivers/ahcid/Cargo.toml + cargo clean --manifest-path drivers/e1000d/Cargo.toml cargo clean --manifest-path drivers/ps2d/Cargo.toml cargo clean --manifest-path drivers/pcid/Cargo.toml cargo clean --manifest-path drivers/vesad/Cargo.toml @@ -32,10 +33,17 @@ clean: cargo clean --manifest-path programs/ion/Cargo.toml cargo clean --manifest-path programs/coreutils/Cargo.toml cargo clean --manifest-path programs/extrautils/Cargo.toml + cargo clean --manifest-path programs/netutils/Cargo.toml + cargo clean --manifest-path programs/orbutils/Cargo.toml cargo clean --manifest-path programs/userutils/Cargo.toml cargo clean --manifest-path programs/smith/Cargo.toml + cargo clean --manifest-path schemes/ethernetd/Cargo.toml cargo clean --manifest-path schemes/example/Cargo.toml + cargo clean --manifest-path schemes/ipd/Cargo.toml + cargo clean --manifest-path schemes/orbital/Cargo.toml cargo clean --manifest-path schemes/redoxfs/Cargo.toml + cargo clean --manifest-path schemes/tcpd/Cargo.toml + cargo clean --manifest-path schemes/udpd/Cargo.toml rm -rf initfs/bin rm -rf filesystem/bin rm -rf build @@ -118,7 +126,7 @@ $(KBUILD)/librustc_unicode.rlib: rust/src/librustc_unicode/lib.rs $(KBUILD)/libc $(KBUILD)/libcollections.rlib: rust/src/libcollections/lib.rs $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/librustc_unicode.rlib $(KRUSTC) $(KRUSTCFLAGS) -o $@ $< -$(KBUILD)/libkernel.a: kernel/** $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/libcollections.rlib $(BUILD)/initfs.rs FORCE +$(KBUILD)/libkernel.a: kernel/** $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/libcollections.rlib $(BUILD)/initfs.rs $(KCARGO) rustc $(KCARGOFLAGS) -C opt-level=s -C lto -o $@ $(KBUILD)/kernel: $(KBUILD)/libkernel.a @@ -170,17 +178,12 @@ initfs/bin/%: schemes/%/Cargo.toml schemes/%/src/** $(BUILD)/libstd.rlib strip $@ rm $@.d -initfs_drivers: \ - initfs/bin/ahcid \ - initfs/bin/pcid - -initfs_schemes: \ - initfs/bin/redoxfs - $(BUILD)/initfs.rs: \ initfs/bin/init \ - initfs_drivers \ - initfs_schemes + initfs/bin/ahcid \ + initfs/bin/pcid \ + initfs/bin/redoxfs \ + initfs/etc/** echo 'use collections::BTreeMap;' > $@ echo 'pub fn gen() -> BTreeMap<&'"'"'static [u8], (&'"'"'static [u8], bool)> {' >> $@ echo ' let mut files: BTreeMap<&'"'"'static [u8], (&'"'"'static [u8], bool)> = BTreeMap::new();' >> $@ @@ -218,6 +221,18 @@ filesystem/bin/%: programs/extrautils/Cargo.toml programs/extrautils/src/bin/%.r strip $@ rm $@.d +filesystem/bin/%: programs/netutils/Cargo.toml programs/netutils/src/%/**.rs $(BUILD)/libstd.rlib + mkdir -p filesystem/bin + $(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@ + strip $@ + rm $@.d + +filesystem/bin/%: programs/orbutils/Cargo.toml programs/orbutils/src/%/**.rs $(BUILD)/libstd.rlib + mkdir -p filesystem/bin + $(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@ + strip $@ + rm $@.d + filesystem/bin/%: programs/userutils/Cargo.toml programs/userutils/src/bin/%.rs $(BUILD)/libstd.rlib mkdir -p filesystem/bin $(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@ @@ -278,6 +293,23 @@ extrautils: \ filesystem/bin/rem \ #filesystem/bin/dmesg filesystem/bin/info filesystem/bin/man filesystem/bin/watch +netutils: \ + filesystem/bin/dhcpd \ + filesystem/bin/dnsd \ + filesystem/bin/irc \ + filesystem/bin/nc \ + filesystem/bin/wget + +orbutils: \ + filesystem/bin/calculator \ + filesystem/bin/character_map \ + filesystem/bin/editor \ + filesystem/bin/file_manager \ + filesystem/bin/launcher \ + filesystem/bin/orblogin \ + filesystem/bin/terminal \ + filesystem/bin/viewer + userutils: \ filesystem/bin/getty \ filesystem/bin/id \ @@ -286,28 +318,35 @@ userutils: \ filesystem/bin/sudo schemes: \ - filesystem/bin/example + filesystem/bin/ethernetd \ + filesystem/bin/example \ + filesystem/bin/ipd \ + filesystem/bin/orbital \ + filesystem/bin/tcpd \ + filesystem/bin/udpd $(BUILD)/filesystem.bin: \ drivers \ coreutils \ extrautils \ + netutils \ + orbutils \ userutils \ schemes \ filesystem/bin/ion \ filesystem/bin/smith rm -rf $@ $(BUILD)/filesystem/ - echo exit | cargo run --manifest-path schemes/redoxfs/Cargo.toml --bin redoxfs-utility $@ 8 + echo exit | cargo run --manifest-path schemes/redoxfs/Cargo.toml --bin redoxfs-utility $@ 64 mkdir -p $(BUILD)/filesystem/ cargo run --manifest-path schemes/redoxfs/Cargo.toml --bin redoxfs-fuse $@ $(BUILD)/filesystem/ & sleep 2 - -cp -RL filesystem/* $(BUILD)/filesystem/ - -chown -R 0:0 $(BUILD)/filesystem/ - -chown -R 1000:1000 $(BUILD)/filesystem/home/user/ - -chmod 700 $(BUILD)/filesystem/root/ - -chmod 700 $(BUILD)/filesystem/home/user/ - -chmod +s $(BUILD)/filesystem/bin/su - -chmod +s $(BUILD)/filesystem/bin/sudo + cp -RL filesystem/* $(BUILD)/filesystem/ + chown -R 0:0 $(BUILD)/filesystem/ + chown -R 1000:1000 $(BUILD)/filesystem/home/user/ + chmod 700 $(BUILD)/filesystem/root/ + chmod 700 $(BUILD)/filesystem/home/user/ + chmod +s $(BUILD)/filesystem/bin/su + chmod +s $(BUILD)/filesystem/bin/sudo sync -fusermount -u $(BUILD)/filesystem/ rm -rf $(BUILD)/filesystem/ @@ -321,3 +360,6 @@ unmount: FORCE sync -fusermount -u $(KBUILD)/harddrive/ rm -rf $(KBUILD)/harddrive/ + +wireshark: FORCE + wireshark $(KBUILD)/network.pcap diff --git a/arch/arm/Cargo.toml b/arch/arm/Cargo.toml index b01e511..deadc75 100644 --- a/arch/arm/Cargo.toml +++ b/arch/arm/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" [dependencies] bitflags = "*" -hole_list_allocator = { path = "../../alloc/hole_list_allocator"} +hole_list_allocator = { path = "../../crates/hole_list_allocator"} spin = "*" diff --git a/arch/x86_64/Cargo.toml b/arch/x86_64/Cargo.toml index 5832241..197f407 100644 --- a/arch/x86_64/Cargo.toml +++ b/arch/x86_64/Cargo.toml @@ -4,10 +4,10 @@ version = "0.1.0" [dependencies] bitflags = "*" -hole_list_allocator = { path = "../../alloc/hole_list_allocator" } -io = { path = "../../drivers/io" } +hole_list_allocator = { path = "../../crates/hole_list_allocator/" } +io = { path = "../../crates/io/" } spin = "*" [dependencies.x86] -version = "*" +version = "0.7" default-features = false diff --git a/arch/x86_64/src/interrupt/irq.rs b/arch/x86_64/src/interrupt/irq.rs index 18cd3c7..ad9151d 100644 --- a/arch/x86_64/src/interrupt/irq.rs +++ b/arch/x86_64/src/interrupt/irq.rs @@ -1,11 +1,11 @@ -use spin::Mutex; use x86::io; use device::serial::{COM1, COM2}; use time; -pub static ACKS: Mutex<[usize; 16]> = Mutex::new([0; 16]); -pub static COUNTS: Mutex<[usize; 16]> = Mutex::new([0; 16]); +extern { + fn irq_trigger(irq: u8); +} #[inline(always)] unsafe fn master_ack() { @@ -27,11 +27,11 @@ pub unsafe fn acknowledge(irq: usize) { } interrupt!(pit, { - COUNTS.lock()[0] += 1; + irq_trigger(0); { const PIT_RATE: u64 = 46500044; - + let mut offset = time::OFFSET.lock(); let sum = offset.1 + PIT_RATE; offset.1 = sum % 1000000000; @@ -42,76 +42,74 @@ interrupt!(pit, { }); interrupt!(keyboard, { - COUNTS.lock()[1] += 1; + irq_trigger(1); }); interrupt!(cascade, { - COUNTS.lock()[2] += 1; + irq_trigger(2); master_ack(); }); interrupt!(com2, { - COUNTS.lock()[3] += 1; + irq_trigger(3); COM2.lock().on_receive(); master_ack(); }); interrupt!(com1, { - COUNTS.lock()[4] += 1; + irq_trigger(4); COM1.lock().on_receive(); master_ack(); }); interrupt!(lpt2, { - COUNTS.lock()[5] += 1; + irq_trigger(5); master_ack(); }); interrupt!(floppy, { - COUNTS.lock()[6] += 1; + irq_trigger(6); master_ack(); }); interrupt!(lpt1, { - COUNTS.lock()[7] += 1; + irq_trigger(7); master_ack(); }); interrupt!(rtc, { - COUNTS.lock()[8] += 1; + irq_trigger(8); slave_ack(); }); interrupt!(pci1, { - COUNTS.lock()[9] += 1; + irq_trigger(9); slave_ack(); }); interrupt!(pci2, { - COUNTS.lock()[10] += 1; - slave_ack(); + irq_trigger(10); }); interrupt!(pci3, { - COUNTS.lock()[11] += 1; - slave_ack(); + irq_trigger(11); }); interrupt!(mouse, { - COUNTS.lock()[12] += 1; + irq_trigger(12); }); interrupt!(fpu, { - COUNTS.lock()[13] += 1; + irq_trigger(13); slave_ack(); }); interrupt!(ata1, { - COUNTS.lock()[14] += 1; + irq_trigger(14); slave_ack(); }); interrupt!(ata2, { - COUNTS.lock()[15] += 1; + irq_trigger(15); slave_ack(); }); diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index 36b45ff..ba51fe5 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -148,7 +148,7 @@ pub unsafe extern fn kstart_ap(cpu_id: usize, _page_table: usize, stack_start: u let kernel_table = KERNEL_TABLE.load(Ordering::SeqCst); // Initialize paging - let (active_table, tcb_offset) = paging::init_ap(cpu_id, stack_start, stack_end, kernel_table); + let (_active_table, tcb_offset) = paging::init_ap(cpu_id, stack_start, stack_end, kernel_table); // Set up GDT for AP gdt::init(tcb_offset, stack_end); diff --git a/bootloader/x86_64/vesa.asm b/bootloader/x86_64/vesa.asm index ca7ebf0..943e4eb 100644 --- a/bootloader/x86_64/vesa.asm +++ b/bootloader/x86_64/vesa.asm @@ -115,8 +115,8 @@ vesa: .minx dw 640 .miny dw 480 .required: -.requiredx dw 0 ; 1024 ;USE THESE WITH CAUTION -.requiredy dw 0 ; 768 +.requiredx dw 1024 ;USE THESE WITH CAUTION +.requiredy dw 768 .requiredmode dw 0 .modeok db ": Is this OK?(y/n)",10,13,0 diff --git a/cakefile.rs b/cakefile.rs deleted file mode 100644 index c5b9b88..0000000 --- a/cakefile.rs +++ /dev/null @@ -1,36 +0,0 @@ -#[macro_use] -extern crate cake; - -const QEMU: &'static str = "qemu-system-x86_64"; - -const LS_FLAGS: &'static [&'static str] = &["-a", "/"]; - -build! { - // ---- COMMANDS ---- - start(harddrive) => {}, - list(kernel_list) => {}, - run(bochs) => {}, - clean() => cmd!("rm", "-rf", "build/*"), - - // ---- RECIPES ---- - bochs(harddrive) => cmd!("bochs", "-f", "bochs.x86_64"), - qemu(harddrive) => cmd!(QEMU, - "-serial", "mon:stdio", - "-drive", "file=build/harddrive.bin,format=raw,index=0,media=disk"), - libkernel() => cmd!("cargo", "rustc", "--", "-C", "lto"), - kernel(libkernel) => cmd!("ld", - "-m", "elf_x86_64", - "--gc-sections", - "-z", "max-page-size=0x1000", - "-T bootloader/x86/kernel.ld", - "-o", "build/kernel.in", "build/libkernel.a"), - kernel_list(kernel) => cmd!("objdump", - "-C", "-M", "intel", - "-D", "build/kernel.bin", - ">", "build/kernel.list"), - harddrive(kernel) => cmd!("nasm", - "-f", "bin", - "-o", "build/harddrive.bin", - "-D", "ARCH_x86_64", "-ibootloader/x86/", "-ibuild/", - "bootloader/x86/harddrive.asm"), -} diff --git a/drivers/dma/Cargo.toml b/crates/dma/Cargo.toml similarity index 100% rename from drivers/dma/Cargo.toml rename to crates/dma/Cargo.toml diff --git a/drivers/dma/src/lib.rs b/crates/dma/src/lib.rs similarity index 100% rename from drivers/dma/src/lib.rs rename to crates/dma/src/lib.rs diff --git a/crates/event/Cargo.toml b/crates/event/Cargo.toml new file mode 100644 index 0000000..124d242 --- /dev/null +++ b/crates/event/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "event" +version = "0.1.0" + +[dependencies.syscall] +path = "../../syscall/" diff --git a/crates/event/src/lib.rs b/crates/event/src/lib.rs new file mode 100644 index 0000000..54c0f34 --- /dev/null +++ b/crates/event/src/lib.rs @@ -0,0 +1,55 @@ +#![feature(question_mark)] + +extern crate syscall; + +use std::collections::BTreeMap; +use std::fs::File; +use std::io::{Read, Error, Result}; +use std::os::unix::io::RawFd; + +pub struct EventQueue { + /// The file to read events from + file: File, + /// A map of registered file descriptors to their handler callbacks + callbacks: BTreeMap Result>>> +} + +impl EventQueue { + /// Create a new event queue + pub fn new() -> Result> { + Ok(EventQueue { + file: File::open("event:")?, + callbacks: BTreeMap::new() + }) + } + + /// Add a file to the event queue, calling a callback when an event occurs + /// + /// The callback is given a mutable reference to the file and the event data + /// (typically the length of data available for read) + /// + /// The callback returns Ok(None) if it wishes to continue the event loop, + /// or Ok(Some(R)) to break the event loop and return the value. + /// Err can be used to allow the callback to return an I/O error, and break the + /// event loop + pub fn add Result> + 'static>(&mut self, fd: RawFd, callback: F) -> Result<()> { + syscall::fevent(fd, syscall::EVENT_READ).map_err(|x| Error::from_sys(x))?; + + self.callbacks.insert(fd, Box::new(callback)); + + Ok(()) + } + + /// Process the event queue until a callback returns Some(R) + pub fn run(&mut self) -> Result { + loop { + let mut event = syscall::Event::default(); + self.file.read(&mut event)?; + if let Some(callback) = self.callbacks.get_mut(&event.id) { + if let Some(ret) = callback(event.data)? { + return Ok(ret); + } + } + } + } +} diff --git a/alloc/hole_list_allocator/Cargo.toml b/crates/hole_list_allocator/Cargo.toml similarity index 100% rename from alloc/hole_list_allocator/Cargo.toml rename to crates/hole_list_allocator/Cargo.toml diff --git a/alloc/hole_list_allocator/src/lib.rs b/crates/hole_list_allocator/src/lib.rs similarity index 100% rename from alloc/hole_list_allocator/src/lib.rs rename to crates/hole_list_allocator/src/lib.rs diff --git a/drivers/io/Cargo.toml b/crates/io/Cargo.toml similarity index 100% rename from drivers/io/Cargo.toml rename to crates/io/Cargo.toml diff --git a/drivers/io/src/io.rs b/crates/io/src/io.rs similarity index 100% rename from drivers/io/src/io.rs rename to crates/io/src/io.rs diff --git a/drivers/io/src/lib.rs b/crates/io/src/lib.rs similarity index 100% rename from drivers/io/src/lib.rs rename to crates/io/src/lib.rs diff --git a/drivers/io/src/mmio.rs b/crates/io/src/mmio.rs similarity index 100% rename from drivers/io/src/mmio.rs rename to crates/io/src/mmio.rs diff --git a/drivers/io/src/pio.rs b/crates/io/src/pio.rs similarity index 100% rename from drivers/io/src/pio.rs rename to crates/io/src/pio.rs diff --git a/crates/resource_scheme/Cargo.toml b/crates/resource_scheme/Cargo.toml new file mode 100644 index 0000000..7320a1a --- /dev/null +++ b/crates/resource_scheme/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "resource_scheme" +version = "0.1.0" + +[dependencies.syscall] +path = "../../syscall/" diff --git a/crates/resource_scheme/src/lib.rs b/crates/resource_scheme/src/lib.rs new file mode 100644 index 0000000..266295f --- /dev/null +++ b/crates/resource_scheme/src/lib.rs @@ -0,0 +1,9 @@ +#![feature(question_mark)] + +extern crate syscall; + +pub use resource::Resource; +pub use scheme::ResourceScheme; + +mod resource; +mod scheme; diff --git a/crates/resource_scheme/src/resource.rs b/crates/resource_scheme/src/resource.rs new file mode 100644 index 0000000..cf2fe5c --- /dev/null +++ b/crates/resource_scheme/src/resource.rs @@ -0,0 +1,52 @@ +use syscall::data::Stat; +use syscall::error::*; + +pub trait Resource { + /// Duplicate the resource + /// Returns `EPERM` if the operation is not supported. + fn dup(&self) -> Result> { + Err(Error::new(EPERM)) + } + + /// Return the path of this resource + /// Returns `EPERM` if the operation is not supported. + fn path(&self, _buf: &mut [u8]) -> Result { + Err(Error::new(EPERM)) + } + + /// Read data to buffer + /// Returns `EPERM` if the operation is not supported. + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(Error::new(EPERM)) + } + + /// Write to resource + /// Returns `EPERM` if the operation is not supported. + fn write(&mut self, _buf: &[u8]) -> Result { + Err(Error::new(EPERM)) + } + + /// Seek to the given offset + /// Returns `ESPIPE` if the operation is not supported. + fn seek(&mut self, _pos: usize, _whence: usize) -> Result { + Err(Error::new(ESPIPE)) + } + + /// Get informations about the resource, such as mode and size + /// Returns `EPERM` if the operation is not supported. + fn stat(&self, _stat: &mut Stat) -> Result { + Err(Error::new(EPERM)) + } + + /// Sync all buffers + /// Returns `EPERM` if the operation is not supported. + fn sync(&mut self) -> Result { + Err(Error::new(EPERM)) + } + + /// Truncate to the given length + /// Returns `EPERM` if the operation is not supported. + fn truncate(&mut self, _len: usize) -> Result { + Err(Error::new(EPERM)) + } +} diff --git a/crates/resource_scheme/src/scheme.rs b/crates/resource_scheme/src/scheme.rs new file mode 100644 index 0000000..308f511 --- /dev/null +++ b/crates/resource_scheme/src/scheme.rs @@ -0,0 +1,109 @@ +use std::slice; + +use syscall::data::{Packet, Stat}; +use syscall::error::*; +use syscall::number::*; + +use super::Resource; + +pub trait ResourceScheme { + fn open_resource(&self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result>; + + fn handle(&self, packet: &mut Packet) { + packet.a = Error::mux(match packet.a { + SYS_OPEN => self.open(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d, packet.uid, packet.gid), + SYS_MKDIR => self.mkdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d as u16, packet.uid, packet.gid), + SYS_RMDIR => self.rmdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.uid, packet.gid), + SYS_UNLINK => self.unlink(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.uid, packet.gid), + + SYS_DUP => self.dup(packet.b), + SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }), + SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }), + SYS_LSEEK => self.seek(packet.b, packet.c, packet.d), + SYS_FEVENT => self.fevent(packet.b, packet.c), + SYS_FPATH => self.fpath(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }), + SYS_FSTAT => self.fstat(packet.b, unsafe { &mut *(packet.c as *mut Stat) }), + SYS_FSYNC => self.fsync(packet.b), + SYS_FTRUNCATE => self.ftruncate(packet.b, packet.c), + SYS_CLOSE => self.close(packet.b), + + _ => Err(Error::new(ENOSYS)) + }); + } + + /* Scheme operations */ + fn open(&self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result { + let resource = self.open_resource(path, flags, uid, gid)?; + let resource_ptr = Box::into_raw(resource); + Ok(resource_ptr as usize) + } + + #[allow(unused_variables)] + fn mkdir(&self, path: &[u8], mode: u16, uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + #[allow(unused_variables)] + fn rmdir(&self, path: &[u8], uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + #[allow(unused_variables)] + fn unlink(&self, path: &[u8], uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + /* Resource operations */ + fn dup(&self, old_id: usize) -> Result { + let old = unsafe { &*(old_id as *const T) }; + let resource = old.dup()?; + let resource_ptr = Box::into_raw(resource); + Ok(resource_ptr as usize) + } + + fn read(&self, id: usize, buf: &mut [u8]) -> Result { + let mut resource = unsafe { &mut *(id as *mut T) }; + resource.read(buf) + } + + fn write(&self, id: usize, buf: &[u8]) -> Result { + let mut resource = unsafe { &mut *(id as *mut T) }; + resource.write(buf) + } + + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { + let mut resource = unsafe { &mut *(id as *mut T) }; + resource.seek(pos, whence) + } + + #[allow(unused_variables)] + fn fevent(&self, id: usize, flags: usize) -> Result { + Err(Error::new(EBADF)) + } + + fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { + let resource = unsafe { &*(id as *const T) }; + resource.path(buf) + } + + fn fstat(&self, id: usize, stat: &mut Stat) -> Result { + let resource = unsafe { &*(id as *const T) }; + resource.stat(stat) + } + + fn fsync(&self, id: usize) -> Result { + let mut resource = unsafe { &mut *(id as *mut T) }; + resource.sync() + } + + fn ftruncate(&self, id: usize, len: usize) -> Result { + let mut resource = unsafe { &mut *(id as *mut T) }; + resource.truncate(len) + } + + fn close(&self, id: usize) -> Result { + let resource = unsafe { Box::from_raw(id as *mut T) }; + drop(resource); + Ok(0) + } +} diff --git a/drivers/ahcid/Cargo.toml b/drivers/ahcid/Cargo.toml index 80725d7..324ea85 100644 --- a/drivers/ahcid/Cargo.toml +++ b/drivers/ahcid/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" [dependencies] bitflags = "*" -dma = { path = "../dma/" } -io = { path = "../io/" } +dma = { path = "../../crates/dma/" } +io = { path = "../../crates/io/" } spin = "*" syscall = { path = "../../syscall/" } diff --git a/drivers/ahcid/src/ahci/hba.rs b/drivers/ahcid/src/ahci/hba.rs index 43d29e1..e5ff7ca 100644 --- a/drivers/ahcid/src/ahci/hba.rs +++ b/drivers/ahcid/src/ahci/hba.rs @@ -25,6 +25,10 @@ const HBA_SIG_ATAPI: u32 = 0xEB140101; const HBA_SIG_PM: u32 = 0x96690101; const HBA_SIG_SEMB: u32 = 0xC33C0101; +fn pause() { + unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } +} + #[derive(Debug)] pub enum HbaPortType { None, @@ -119,7 +123,9 @@ impl HbaPort { cmdfis.counth.write(0); } - while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {} + while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) { + pause(); + } self.ci.writef(1 << slot, true); @@ -127,6 +133,7 @@ impl HbaPort { if self.is.readf(HBA_PORT_IS_TFES) { return None; } + pause(); } if self.is.readf(HBA_PORT_IS_TFES) { @@ -194,7 +201,9 @@ impl HbaPort { } pub fn start(&mut self) { - while self.cmd.readf(HBA_PORT_CMD_CR) {} + while self.cmd.readf(HBA_PORT_CMD_CR) { + pause(); + } self.cmd.writef(HBA_PORT_CMD_FRE, true); self.cmd.writef(HBA_PORT_CMD_ST, true); @@ -203,7 +212,9 @@ impl HbaPort { pub fn stop(&mut self) { self.cmd.writef(HBA_PORT_CMD_ST, false); - while self.cmd.readf(HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) {} + while self.cmd.readf(HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) { + pause(); + } self.cmd.writef(HBA_PORT_CMD_FRE, false); } @@ -267,7 +278,9 @@ impl HbaPort { cmdfis.counth.write((sectors >> 8) as u8); } - while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {} + while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) { + pause(); + } self.ci.writef(1 << slot, true); @@ -276,6 +289,7 @@ impl HbaPort { println!("IS_TFES set in CI loop TFS {:X} SERR {:X}", self.tfd.read(), self.serr.read()); return Err(Error::new(EIO)); } + pause(); } if self.is.readf(HBA_PORT_IS_TFES) { @@ -312,7 +326,9 @@ pub struct HbaMem { impl HbaMem { pub fn reset(&mut self) { self.ghc.writef(1, true); - while self.ghc.readf(1) {} + while self.ghc.readf(1) { + pause(); + } } } diff --git a/drivers/ahcid/src/main.rs b/drivers/ahcid/src/main.rs index 7f5ccda..880177b 100644 --- a/drivers/ahcid/src/main.rs +++ b/drivers/ahcid/src/main.rs @@ -8,10 +8,11 @@ extern crate io; extern crate spin; extern crate syscall; +use std::{env, thread, usize}; use std::fs::File; use std::io::{Read, Write}; -use std::{env, thread, usize}; -use syscall::{iopl, physmap, physunmap, MAP_WRITE, Packet, Scheme}; +use std::os::unix::io::AsRawFd; +use syscall::{EVENT_READ, MAP_WRITE, Event, Packet, Scheme}; use scheme::DiskScheme; @@ -29,21 +30,42 @@ fn main() { thread::spawn(move || { unsafe { - iopl(3).expect("ahcid: failed to get I/O permission"); + syscall::iopl(3).expect("ahcid: failed to get I/O permission"); asm!("cli" :::: "intel", "volatile"); } - let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") }; + let address = unsafe { syscall::physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") }; { let mut socket = File::create(":disk").expect("ahcid: failed to create disk scheme"); + let socket_fd = socket.as_raw_fd(); + syscall::fevent(socket_fd, EVENT_READ).expect("ahcid: failed to fevent disk scheme"); + + let mut irq_file = File::open(&format!("irq:{}", irq)).expect("ahcid: failed to open irq file"); + let irq_fd = irq_file.as_raw_fd(); + syscall::fevent(irq_fd, EVENT_READ).expect("ahcid: failed to fevent irq file"); + + let mut event_file = File::open("event:").expect("ahcid: failed to open event file"); + let scheme = DiskScheme::new(ahci::disks(address, irq)); loop { - let mut packet = Packet::default(); - socket.read(&mut packet).expect("ahcid: failed to read disk scheme"); - scheme.handle(&mut packet); - socket.write(&mut packet).expect("ahcid: failed to read disk scheme"); + let mut event = Event::default(); + event_file.read(&mut event).expect("ahcid: failed to read event file"); + if event.id == socket_fd { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("ahcid: failed to read disk scheme"); + scheme.handle(&mut packet); + socket.write(&mut packet).expect("ahcid: failed to write disk scheme"); + } else if event.id == irq_fd { + let mut irq = [0; 8]; + if irq_file.read(&mut irq).expect("ahcid: failed to read irq file") >= irq.len() { + println!("IRQ"); + irq_file.write(&irq).expect("ahcid: failed to write irq file"); + } + } else { + println!("Unknown event {}", event.id); + } } } - unsafe { let _ = physunmap(address); } + unsafe { let _ = syscall::physunmap(address); } }); } diff --git a/drivers/ahcid/src/scheme.rs b/drivers/ahcid/src/scheme.rs index eb2ec12..790e920 100644 --- a/drivers/ahcid/src/scheme.rs +++ b/drivers/ahcid/src/scheme.rs @@ -3,7 +3,7 @@ use std::{cmp, str}; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use spin::Mutex; -use syscall::{Error, EBADF, EINVAL, ENOENT, Result, Scheme, Stat, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::{Error, EACCES, EBADF, EINVAL, ENOENT, Result, Scheme, Stat, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; use ahci::disk::Disk; @@ -29,17 +29,21 @@ impl DiskScheme { } impl Scheme for DiskScheme { - fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { - let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; + fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { + if uid == 0 { + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; - let i = path_str.parse::().or(Err(Error::new(ENOENT)))?; + let i = path_str.parse::().or(Err(Error::new(ENOENT)))?; - if let Some(disk) = self.disks.get(i) { - let id = self.next_id.fetch_add(1, Ordering::SeqCst); - self.handles.lock().insert(id, (disk.clone(), 0)); - Ok(id) + if let Some(disk) = self.disks.get(i) { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.lock().insert(id, (disk.clone(), 0)); + Ok(id) + } else { + Err(Error::new(ENOENT)) + } } else { - Err(Error::new(ENOENT)) + Err(Error::new(EACCES)) } } diff --git a/drivers/e1000d/Cargo.toml b/drivers/e1000d/Cargo.toml index eacd0d1..01413fe 100644 --- a/drivers/e1000d/Cargo.toml +++ b/drivers/e1000d/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" [dependencies] bitflags = "*" -dma = { path = "../dma/" } -io = { path = "../io/" } +dma = { path = "../../crates/dma/" } +event = { path = "../../crates/event/" } +io = { path = "../../crates/io/" } spin = "*" syscall = { path = "../../syscall/" } diff --git a/drivers/e1000d/src/device.rs b/drivers/e1000d/src/device.rs index ffe8774..6674c7c 100644 --- a/drivers/e1000d/src/device.rs +++ b/drivers/e1000d/src/device.rs @@ -1,7 +1,7 @@ use std::{cmp, mem, ptr, slice}; use dma::Dma; -use syscall::error::Result; +use syscall::error::{Error, EACCES, EWOULDBLOCK, Result}; use syscall::scheme::Scheme; const CTRL: u32 = 0x00; @@ -96,12 +96,16 @@ pub struct Intel8254x { receive_buffer: [Dma<[u8; 16384]>; 16], receive_ring: Dma<[Rd; 16]>, transmit_buffer: [Dma<[u8; 16384]>; 16], - transmit_ring: Dma<[Td; 16]>, + transmit_ring: Dma<[Td; 16]> } impl Scheme for Intel8254x { - fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { - Ok(0) + fn open(&self, _path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { + if uid == 0 { + Ok(0) + } else { + Err(Error::new(EACCES)) + } } fn dup(&self, id: usize) -> Result { @@ -109,7 +113,15 @@ impl Scheme for Intel8254x { } fn read(&self, _id: usize, buf: &mut [u8]) -> Result { - for tail in 0..self.receive_ring.len() { + let head = unsafe { self.read(RDH) }; + let mut tail = unsafe { self.read(RDT) }; + + tail += 1; + if tail >= self.receive_ring.len() as u32 { + tail = 0; + } + + if tail != head { let rd = unsafe { &mut * (self.receive_ring.as_ptr().offset(tail as isize) as *mut Rd) }; if rd.status & RD_DD == RD_DD { rd.status = 0; @@ -121,11 +133,14 @@ impl Scheme for Intel8254x { buf[i] = data[i]; i += 1; } + + unsafe { self.write(RDT, tail) }; + return Ok(i); } } - Ok(0) + Err(Error::new(EWOULDBLOCK)) } fn write(&self, _id: usize, buf: &[u8]) -> Result { @@ -166,8 +181,12 @@ impl Scheme for Intel8254x { return Ok(i); } - } + unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } + } + } + + fn fsync(&self, _id: usize) -> Result { Ok(0) } @@ -198,6 +217,11 @@ impl Intel8254x { Ok(module) } + pub unsafe fn irq(&self) -> bool { + let icr = self.read(ICR); + icr != 0 + } + pub unsafe fn read(&self, register: u32) -> u32 { ptr::read_volatile((self.base + register as usize) as *mut u32) } @@ -271,8 +295,7 @@ impl Intel8254x { self.write(TDH, 0); self.write(TDT, 0); - //self.write(IMS, IMS_RXT | IMS_RX | IMS_RXDMT | IMS_RXSEQ | IMS_LSC | IMS_TXQE | IMS_TXDW); - self.write(IMS, 0); + self.write(IMS, IMS_RXT | IMS_RX | IMS_RXDMT | IMS_RXSEQ); // | IMS_LSC | IMS_TXQE | IMS_TXDW self.flag(RCTL, RCTL_EN, true); self.flag(RCTL, RCTL_UPE, true); diff --git a/drivers/e1000d/src/main.rs b/drivers/e1000d/src/main.rs index 9156aab..3b097df 100644 --- a/drivers/e1000d/src/main.rs +++ b/drivers/e1000d/src/main.rs @@ -2,13 +2,19 @@ #![feature(question_mark)] extern crate dma; +extern crate event; extern crate syscall; +use std::cell::RefCell; use std::{env, thread}; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{Read, Write, Result}; +use std::os::unix::io::AsRawFd; +use std::sync::Arc; -use syscall::{iopl, physmap, physunmap, Packet, Scheme, MAP_WRITE}; +use event::EventQueue; +use syscall::{Packet, Scheme, MAP_WRITE}; +use syscall::error::EWOULDBLOCK; pub mod device; @@ -23,21 +29,66 @@ fn main() { thread::spawn(move || { unsafe { - iopl(3).expect("e1000d: failed to get I/O permission"); + syscall::iopl(3).expect("e1000d: failed to get I/O permission"); asm!("cli" :::: "intel", "volatile"); } - let address = unsafe { physmap(bar, 128*1024, MAP_WRITE).expect("e1000d: failed to map address") }; + let socket = Arc::new(RefCell::new(File::create(":network").expect("e1000d: failed to create network scheme"))); + + let address = unsafe { syscall::physmap(bar, 128*1024, MAP_WRITE).expect("e1000d: failed to map address") }; { - let mut device = unsafe { device::Intel8254x::new(address, irq).expect("e1000d: failed to allocate device") }; - let mut socket = File::create(":network").expect("e1000d: failed to create network scheme"); - loop { + let device = Arc::new(unsafe { device::Intel8254x::new(address, irq).expect("e1000d: failed to allocate device") }); + + let mut event_queue = EventQueue::<()>::new().expect("e1000d: failed to create event queue"); + + let todo = Arc::new(RefCell::new(Vec::::new())); + + let device_irq = device.clone(); + let socket_irq = socket.clone(); + let todo_irq = todo.clone(); + let mut irq_file = File::open(format!("irq:{}", irq)).expect("e1000d: failed to open IRQ file"); + event_queue.add(irq_file.as_raw_fd(), move |_count: usize| -> Result> { + let mut irq = [0; 8]; + irq_file.read(&mut irq)?; + if unsafe { device_irq.irq() } { + irq_file.write(&mut irq)?; + + let mut todo = todo_irq.borrow_mut(); + let mut i = 0; + while i < todo.len() { + let a = todo[i].a; + device_irq.handle(&mut todo[i]); + if todo[i].a == (-EWOULDBLOCK) as usize { + todo[i].a = a; + i += 1; + } else { + socket_irq.borrow_mut().write(&mut todo[i])?; + todo.remove(i); + } + } + } + Ok(None) + }).expect("e1000d: failed to catch events on IRQ file"); + + let socket_fd = socket.borrow().as_raw_fd(); + event_queue.add(socket_fd, move |_count: usize| -> Result> { let mut packet = Packet::default(); - socket.read(&mut packet).expect("e1000d: failed to read network scheme"); + socket.borrow_mut().read(&mut packet)?; + + let a = packet.a; device.handle(&mut packet); - socket.write(&mut packet).expect("e1000d: failed to read network scheme"); - } + if packet.a == (-EWOULDBLOCK) as usize { + packet.a = a; + todo.borrow_mut().push(packet); + } else { + socket.borrow_mut().write(&mut packet)?; + } + + Ok(None) + }).expect("e1000d: failed to catch events on IRQ file"); + + event_queue.run().expect("e1000d: failed to handle events"); } - unsafe { let _ = physunmap(address); } + unsafe { let _ = syscall::physunmap(address); } }); } diff --git a/drivers/ps2d/Cargo.toml b/drivers/ps2d/Cargo.toml index 665e3c1..31bb252 100644 --- a/drivers/ps2d/Cargo.toml +++ b/drivers/ps2d/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" [dependencies] bitflags = "*" -io = { path = "../io/" } -spin = "*" +io = { path = "../../crates/io/" } +orbclient = "0.1" syscall = { path = "../../syscall/" } diff --git a/drivers/ps2d/src/keyboard.rs b/drivers/ps2d/src/keyboard.rs index 8eb2858..5384d69 100644 --- a/drivers/ps2d/src/keyboard.rs +++ b/drivers/ps2d/src/keyboard.rs @@ -1,7 +1,8 @@ use std::fs::File; use std::io::{Read, Write}; use std::mem; -use std::thread; + +use orbclient::KeyEvent; use keymap; @@ -9,7 +10,6 @@ pub fn keyboard() { let mut file = File::open("irq:1").expect("ps2d: failed to open irq:1"); let mut input = File::open("display:input").expect("ps2d: failed to open display:input"); - let mut ctrl = false; let mut lshift = false; let mut rshift = false; loop { @@ -28,71 +28,17 @@ pub fn keyboard() { (data, true) }; - if scancode == 0x1D { - ctrl = pressed; - } else if scancode == 0x2A { + if scancode == 0x2A { lshift = pressed; } else if scancode == 0x36 { rshift = pressed; - } else if pressed { - match scancode { - f @ 0x3B ... 0x44 => { // F1 through F10 - input.write(&[(f - 0x3B) + 0xF4]).unwrap(); - }, - 0x57 => { // F11 - input.write(&[0xFE]).unwrap(); - }, - 0x58 => { // F12 - input.write(&[0xFF]).unwrap(); - }, - 0x47 => { // Home - input.write(b"\x1B[H").unwrap(); - }, - 0x48 => { // Up - input.write(b"\x1B[A").unwrap(); - }, - 0x49 => { // Page up - input.write(b"\x1B[5~").unwrap(); - }, - 0x4B => { // Left - input.write(b"\x1B[D").unwrap(); - }, - 0x4D => { // Right - input.write(b"\x1B[C").unwrap(); - }, - 0x4F => { // End - input.write(b"\x1B[F").unwrap(); - }, - 0x50 => { // Down - input.write(b"\x1B[B").unwrap(); - }, - 0x51 => { // Page down - input.write(b"\x1B[6~").unwrap(); - }, - 0x52 => { // Insert - input.write(b"\x1B[2~").unwrap(); - }, - 0x53 => { // Delete - input.write(b"\x1B[3~").unwrap(); - }, - _ => { - let c = if ctrl { - match keymap::get_char(scancode, false) { - c @ 'a' ... 'z' => ((c as u8 - b'a') + b'\x01') as char, - c => c - } - } else { - keymap::get_char(scancode, lshift || rshift) - }; - - if c != '\0' { - input.write(&[c as u8]).unwrap(); - } - } - } } - } else { - thread::yield_now(); + + input.write(&KeyEvent { + character: keymap::get_char(scancode, lshift || rshift), + scancode: scancode, + pressed: pressed + }.to_event()).expect("ps2d: failed to write key event"); } } } diff --git a/drivers/ps2d/src/main.rs b/drivers/ps2d/src/main.rs index 4a3888e..60d1bfc 100644 --- a/drivers/ps2d/src/main.rs +++ b/drivers/ps2d/src/main.rs @@ -3,6 +3,7 @@ #[macro_use] extern crate bitflags; extern crate io; +extern crate orbclient; extern crate syscall; use std::thread; diff --git a/drivers/ps2d/src/mouse.rs b/drivers/ps2d/src/mouse.rs index f273de9..f16c9c7 100644 --- a/drivers/ps2d/src/mouse.rs +++ b/drivers/ps2d/src/mouse.rs @@ -1,7 +1,8 @@ use std::fs::File; use std::io::{Read, Write}; use std::mem; -use std::thread; + +use orbclient::MouseEvent; bitflags! { flags MousePacketFlags: u8 { @@ -18,6 +19,7 @@ bitflags! { pub fn mouse(extra_packet: bool) { let mut file = File::open("irq:12").expect("ps2d: failed to open irq:12"); + let mut input = File::open("display:input").expect("ps2d: failed to open display:input"); let mut packets = [0; 4]; let mut packet_i = 0; @@ -40,23 +42,29 @@ pub fn mouse(extra_packet: bool) { packet_i = 0; } else if packet_i >= packets.len() || (!extra_packet && packet_i >= 3) { if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) { - let mut dx = packets[1] as isize; + let mut dx = packets[1] as i32; if flags.contains(X_SIGN) { dx -= 0x100; } - let mut dy = packets[2] as isize; + let mut dy = -(packets[2] as i32); if flags.contains(Y_SIGN) { - dy -= 0x100; + dy += 0x100; } - let extra = if extra_packet { + let _extra = if extra_packet { packets[3] } else { 0 }; - print!("ps2d: IRQ {:?}, {}, {}, {}\n", flags, dx, dy, extra); + input.write(&MouseEvent { + x: dx, + y: dy, + left_button: flags.contains(LEFT_BUTTON), + middle_button: flags.contains(MIDDLE_BUTTON), + right_button: flags.contains(RIGHT_BUTTON) + }.to_event()).expect("ps2d: failed to write mouse event"); } else { println!("ps2d: overflow {:X} {:X} {:X} {:X}", packets[0], packets[1], packets[2], packets[3]); } @@ -66,8 +74,6 @@ pub fn mouse(extra_packet: bool) { } file.write(&irqs).expect("ps2d: failed to write irq:12"); - } else { - thread::yield_now(); } } } diff --git a/drivers/vesad/Cargo.toml b/drivers/vesad/Cargo.toml index ebfdf0b..f0dcc23 100644 --- a/drivers/vesad/Cargo.toml +++ b/drivers/vesad/Cargo.toml @@ -3,6 +3,7 @@ name = "vesad" version = "0.1.0" [dependencies] +orbclient = "0.1" ransid = { git = "https://github.com/redox-os/ransid.git", branch = "new_api" } rusttype = { git = "https://github.com/dylanede/rusttype.git", optional = true } syscall = { path = "../../syscall/" } diff --git a/drivers/vesad/src/main.rs b/drivers/vesad/src/main.rs index 57a13b3..167c111 100644 --- a/drivers/vesad/src/main.rs +++ b/drivers/vesad/src/main.rs @@ -4,6 +4,7 @@ #![feature(question_mark)] extern crate alloc; +extern crate orbclient; extern crate syscall; use std::fs::File; diff --git a/drivers/vesad/src/scheme.rs b/drivers/vesad/src/scheme.rs index 4c9155d..1e48cb2 100644 --- a/drivers/vesad/src/scheme.rs +++ b/drivers/vesad/src/scheme.rs @@ -1,26 +1,26 @@ use std::cell::{Cell, RefCell}; use std::collections::BTreeMap; -use std::str; +use std::{mem, slice, str}; -use syscall::{Result, Error, EBADF, ENOENT, Scheme}; +use orbclient::{Event, EventOption}; +use syscall::{Result, Error, EACCES, EBADF, ENOENT, Scheme}; use display::Display; -use screen::TextScreen; +use screen::{Screen, GraphicScreen, TextScreen}; pub struct DisplayScheme { width: usize, height: usize, onscreen: usize, active: Cell, - screens: RefCell> + screens: RefCell>> } impl DisplayScheme { pub fn new(width: usize, height: usize, onscreen: usize) -> DisplayScheme { - let mut screens = BTreeMap::new(); - for i in 1..7 { - screens.insert(i, TextScreen::new(Display::new(width, height, onscreen))); - } + let mut screens: BTreeMap> = BTreeMap::new(); + screens.insert(1, Box::new(TextScreen::new(Display::new(width, height, onscreen)))); + screens.insert(2, Box::new(GraphicScreen::new(Display::new(width, height, onscreen)))); DisplayScheme { width: width, @@ -42,9 +42,13 @@ impl DisplayScheme { } impl Scheme for DisplayScheme { - fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { + fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { if path == b"input" { - Ok(0) + if uid == 0 { + Ok(0) + } else { + Err(Error::new(EACCES)) + } } else { let path_str = str::from_utf8(path).unwrap_or(""); let id = path_str.parse::().unwrap_or(0); @@ -63,9 +67,7 @@ impl Scheme for DisplayScheme { fn fevent(&self, id: usize, flags: usize) -> Result { let mut screens = self.screens.borrow_mut(); if let Some(mut screen) = screens.get_mut(&id) { - println!("fevent {:X}", flags); - screen.requested = flags; - Ok(0) + screen.event(flags) } else { Err(Error::new(EBADF)) } @@ -76,7 +78,7 @@ impl Scheme for DisplayScheme { let path_str = if id == 0 { format!("display:input") } else if let Some(screen) = screens.get(&id) { - format!("display:{}/{}/{}", id, screen.console.w, screen.console.h) + format!("display:{}/{}/{}", id, screen.width(), screen.height()) } else { return Err(Error::new(EBADF)); }; @@ -124,11 +126,39 @@ impl Scheme for DisplayScheme { } Ok(1) } else { - if let Some(mut screen) = screens.get_mut(&self.active.get()) { - screen.input(buf) - } else { - Err(Error::new(EBADF)) + let events = unsafe { slice::from_raw_parts(buf.as_ptr() as *const Event, buf.len()/mem::size_of::()) }; + + for event in events.iter() { + let new_active_opt = if let EventOption::Key(key_event) = event.to_option() { + match key_event.scancode { + f @ 0x3B ... 0x44 => { // F1 through F10 + Some((f - 0x3A) as usize) + }, + 0x57 => { // F11 + Some(11) + }, + 0x58 => { // F12 + Some(12) + }, + _ => None + } + } else { + None + }; + + if let Some(new_active) = new_active_opt { + if let Some(mut screen) = screens.get_mut(&new_active) { + self.active.set(new_active); + screen.redraw(); + } + } else { + if let Some(mut screen) = screens.get_mut(&self.active.get()) { + screen.input(event); + } + } } + + Ok(events.len() * mem::size_of::()) } } else if let Some(mut screen) = screens.get_mut(&id) { screen.write(buf, id == self.active.get()) @@ -137,6 +167,15 @@ impl Scheme for DisplayScheme { } } + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { + let mut screens = self.screens.borrow_mut(); + if let Some(mut screen) = screens.get_mut(&id) { + screen.seek(pos, whence) + } else { + Err(Error::new(EBADF)) + } + } + fn close(&self, _id: usize) -> Result { Ok(0) } diff --git a/drivers/vesad/src/screen/graphic.rs b/drivers/vesad/src/screen/graphic.rs index 9e6b71a..11a6e2e 100644 --- a/drivers/vesad/src/screen/graphic.rs +++ b/drivers/vesad/src/screen/graphic.rs @@ -1 +1,118 @@ -pub struct GraphicScreen; +use std::collections::VecDeque; +use std::{cmp, mem, slice}; + +use orbclient::{Event, EventOption}; +use syscall::error::*; +use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END}; + +use display::Display; +use primitive::fast_copy; +use screen::Screen; + +pub struct GraphicScreen { + pub display: Display, + pub seek: usize, + pub mouse_x: i32, + pub mouse_y: i32, + pub input: VecDeque, + pub requested: usize +} + +impl GraphicScreen { + pub fn new(display: Display) -> GraphicScreen { + GraphicScreen { + display: display, + seek: 0, + mouse_x: 0, + mouse_y: 0, + input: VecDeque::new(), + requested: 0 + } + } +} + +impl Screen for GraphicScreen { + fn width(&self) -> usize { + self.display.width + } + + fn height(&self) -> usize { + self.display.height + } + + fn event(&mut self, flags: usize) -> Result { + self.requested = flags; + Ok(0) + } + + fn input(&mut self, event: &Event) { + if let EventOption::Mouse(mut mouse_event) = event.to_option() { + let x = cmp::max(0, cmp::min(self.display.width as i32, self.mouse_x + mouse_event.x)); + let y = cmp::max(0, cmp::min(self.display.height as i32, self.mouse_y + mouse_event.y)); + + mouse_event.x = x; + self.mouse_x = x; + mouse_event.y = y; + self.mouse_y = y; + + self.input.push_back(mouse_event.to_event()); + } else { + self.input.push_back(*event); + } + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + + let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::()) }; + + while i < event_buf.len() && ! self.input.is_empty() { + event_buf[i] = self.input.pop_front().unwrap(); + i += 1; + } + + Ok(i * mem::size_of::()) + } + + fn will_block(&self) -> bool { + self.input.is_empty() + } + + fn write(&mut self, buf: &[u8], sync: bool) -> Result { + let size = cmp::max(0, cmp::min(self.display.offscreen.len() as isize - self.seek as isize, (buf.len()/4) as isize)) as usize; + + if size > 0 { + unsafe { + fast_copy(self.display.offscreen.as_mut_ptr().offset(self.seek as isize) as *mut u8, buf.as_ptr(), size * 4); + if sync { + fast_copy(self.display.onscreen.as_mut_ptr().offset(self.seek as isize) as *mut u8, buf.as_ptr(), size * 4); + } + } + } + + Ok(size * 4) + } + + fn seek(&mut self, pos: usize, whence: usize) -> Result { + let size = self.display.offscreen.len(); + + self.seek = match whence { + SEEK_SET => cmp::min(size, (pos/4)), + SEEK_CUR => cmp::max(0, cmp::min(size as isize, self.seek as isize + (pos/4) as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(size as isize, size as isize + (pos/4) as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(self.seek * 4) + } + + fn sync(&mut self) { + self.redraw(); + } + + fn redraw(&mut self) { + let width = self.display.width; + let height = self.display.height; + self.display.sync(0, 0, width, height); + } +} diff --git a/drivers/vesad/src/screen/mod.rs b/drivers/vesad/src/screen/mod.rs index 01e1583..6a0d5fd 100644 --- a/drivers/vesad/src/screen/mod.rs +++ b/drivers/vesad/src/screen/mod.rs @@ -1,10 +1,30 @@ pub use self::graphic::GraphicScreen; pub use self::text::TextScreen; +use orbclient::Event; +use syscall::Result; + mod graphic; mod text; -pub enum Screen { - Graphic(GraphicScreen), - Text(TextScreen) +pub trait Screen { + fn width(&self) -> usize; + + fn height(&self) -> usize; + + fn event(&mut self, flags: usize) -> Result; + + fn input(&mut self, event: &Event); + + fn read(&mut self, buf: &mut [u8]) -> Result; + + fn will_block(&self) -> bool; + + fn write(&mut self, buf: &[u8], sync: bool) -> Result; + + fn seek(&mut self, pos: usize, whence: usize) -> Result; + + fn sync(&mut self); + + fn redraw(&mut self); } diff --git a/drivers/vesad/src/screen/text.rs b/drivers/vesad/src/screen/text.rs index bf187b7..d61fe7a 100644 --- a/drivers/vesad/src/screen/text.rs +++ b/drivers/vesad/src/screen/text.rs @@ -2,15 +2,17 @@ extern crate ransid; use std::collections::{BTreeSet, VecDeque}; -use self::ransid::{Console, Event}; +use orbclient::{Event, EventOption}; use syscall::Result; use display::Display; +use screen::Screen; pub struct TextScreen { - pub console: Console, + pub console: ransid::Console, pub display: Display, pub changed: BTreeSet, + pub ctrl: bool, pub input: VecDeque, pub end_of_input: bool, pub cooked: VecDeque, @@ -20,17 +22,88 @@ pub struct TextScreen { impl TextScreen { pub fn new(display: Display) -> TextScreen { TextScreen { - console: Console::new(display.width/8, display.height/16), + console: ransid::Console::new(display.width/8, display.height/16), display: display, changed: BTreeSet::new(), + ctrl: false, input: VecDeque::new(), end_of_input: false, cooked: VecDeque::new(), requested: 0 } } +} + +impl Screen for TextScreen { + fn width(&self) -> usize { + self.console.w + } + + fn height(&self) -> usize { + self.console.h + } + + fn event(&mut self, flags: usize) -> Result { + self.requested = flags; + Ok(0) + } + + fn input(&mut self, event: &Event) { + let mut buf = vec![]; + + match event.to_option() { + EventOption::Key(key_event) => { + if key_event.scancode == 0x1D { + self.ctrl = key_event.pressed; + } else if key_event.pressed { + match key_event.scancode { + 0x47 => { // Home + buf.extend_from_slice(b"\x1B[H"); + }, + 0x48 => { // Up + buf.extend_from_slice(b"\x1B[A"); + }, + 0x49 => { // Page up + buf.extend_from_slice(b"\x1B[5~"); + }, + 0x4B => { // Left + buf.extend_from_slice(b"\x1B[D"); + }, + 0x4D => { // Right + buf.extend_from_slice(b"\x1B[C"); + }, + 0x4F => { // End + buf.extend_from_slice(b"\x1B[F"); + }, + 0x50 => { // Down + buf.extend_from_slice(b"\x1B[B"); + }, + 0x51 => { // Page down + buf.extend_from_slice(b"\x1B[6~"); + }, + 0x52 => { // Insert + buf.extend_from_slice(b"\x1B[2~"); + }, + 0x53 => { // Delete + buf.extend_from_slice(b"\x1B[3~"); + }, + _ => { + let c = match key_event.character { + c @ 'A' ... 'Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char, + c @ 'a' ... 'z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char, + c => c + }; + + if c != '\0' { + buf.extend_from_slice(&[c as u8]); + } + } + } + } + }, + _ => () //TODO: Mouse in terminal + } - pub fn input(&mut self, buf: &[u8]) -> Result { if self.console.raw_mode { for &b in buf.iter() { self.input.push_back(b); @@ -40,11 +113,11 @@ impl TextScreen { match b { b'\x03' => { self.end_of_input = true; - self.write(b"^C\n", true)?; + let _ = self.write(b"^C\n", true); }, b'\x08' | b'\x7F' => { if let Some(_c) = self.cooked.pop_back() { - self.write(b"\x08", true)?; + let _ = self.write(b"\x08", true); } }, b'\n' | b'\r' => { @@ -52,19 +125,18 @@ impl TextScreen { while let Some(c) = self.cooked.pop_front() { self.input.push_back(c); } - self.write(b"\n", true)?; + let _ = self.write(b"\n", true); }, _ => { self.cooked.push_back(b); - self.write(&[b], true)?; + let _ = self.write(&[b], true); } } } } - Ok(buf.len()) } - pub fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> Result { let mut i = 0; while i < buf.len() && ! self.input.is_empty() { @@ -79,11 +151,11 @@ impl TextScreen { Ok(i) } - pub fn will_block(&self) -> bool { + fn will_block(&self) -> bool { self.input.is_empty() && ! self.end_of_input } - pub fn write(&mut self, buf: &[u8], sync: bool) -> Result { + fn write(&mut self, buf: &[u8], sync: bool) -> Result { if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h { let x = self.console.x; let y = self.console.y; @@ -97,17 +169,17 @@ impl TextScreen { let changed = &mut self.changed; self.console.write(buf, |event| { match event { - Event::Char { x, y, c, color, bold, .. } => { + ransid::Event::Char { x, y, c, color, bold, .. } => { display.char(x * 8, y * 16, c, color.data, bold, false); changed.insert(y); }, - Event::Rect { x, y, w, h, color } => { + ransid::Event::Rect { x, y, w, h, color } => { display.rect(x * 8, y * 16, w * 8, h * 16, color.data); for y2 in y..y + h { changed.insert(y2); } }, - Event::Scroll { rows, color } => { + ransid::Event::Scroll { rows, color } => { display.scroll(rows * 16, color.data); for y in 0..display.height/16 { changed.insert(y); @@ -132,7 +204,11 @@ impl TextScreen { Ok(buf.len()) } - pub fn sync(&mut self) { + fn seek(&mut self, pos: usize, whence: usize) -> Result { + Ok(0) + } + + fn sync(&mut self) { let width = self.display.width; for change in self.changed.iter() { self.display.sync(0, change * 16, width, 16); @@ -140,7 +216,7 @@ impl TextScreen { self.changed.clear(); } - pub fn redraw(&mut self) { + fn redraw(&mut self) { let width = self.display.width; let height = self.display.height; self.display.sync(0, 0, width, height); diff --git a/filesystem/etc/net/dns b/filesystem/etc/net/dns new file mode 100644 index 0000000..4396cda --- /dev/null +++ b/filesystem/etc/net/dns @@ -0,0 +1 @@ +10.0.2.3 diff --git a/filesystem/etc/net/ip b/filesystem/etc/net/ip new file mode 100644 index 0000000..b86c8a7 --- /dev/null +++ b/filesystem/etc/net/ip @@ -0,0 +1 @@ +10.0.2.15 diff --git a/filesystem/etc/net/ip_router b/filesystem/etc/net/ip_router new file mode 100644 index 0000000..2e66646 --- /dev/null +++ b/filesystem/etc/net/ip_router @@ -0,0 +1 @@ +10.0.2.2 diff --git a/filesystem/etc/net/ip_subnet b/filesystem/etc/net/ip_subnet new file mode 100644 index 0000000..d30f9e9 --- /dev/null +++ b/filesystem/etc/net/ip_subnet @@ -0,0 +1 @@ +255.255.255.0 diff --git a/filesystem/etc/net/mac b/filesystem/etc/net/mac new file mode 100644 index 0000000..9fb01a8 --- /dev/null +++ b/filesystem/etc/net/mac @@ -0,0 +1 @@ +52.54.00.12.34.56 diff --git a/filesystem/etc/orbital.conf b/filesystem/etc/orbital.conf new file mode 100644 index 0000000..8d46aba --- /dev/null +++ b/filesystem/etc/orbital.conf @@ -0,0 +1,2 @@ +background=/ui/background.png +cursor=/ui/cursor.png diff --git a/filesystem/ui b/filesystem/ui new file mode 160000 index 0000000..d371b3c --- /dev/null +++ b/filesystem/ui @@ -0,0 +1 @@ +Subproject commit d371b3c396dfd92f7708de0f7c7c12b9db2c3a8b diff --git a/initfs/etc/init.rc b/initfs/etc/init.rc index d5530ee..43fbc91 100644 --- a/initfs/etc/init.rc +++ b/initfs/etc/init.rc @@ -3,9 +3,9 @@ initfs:bin/redoxfs disk:0 initfs:bin/pcid file:etc/pcid.toml file:bin/vesad file:bin/ps2d +file:bin/ethernetd +file:bin/ipd +file:bin/tcpd +file:bin/udpd file:bin/getty display:1 -file:bin/getty display:2 -file:bin/getty display:3 -file:bin/getty display:4 -file:bin/getty display:5 -file:bin/getty display:6 +file:bin/orbital display:2 diff --git a/kernel/context/context.rs b/kernel/context/context.rs index 8c09573..1093f17 100644 --- a/kernel/context/context.rs +++ b/kernel/context/context.rs @@ -1,12 +1,13 @@ use alloc::arc::Arc; use alloc::boxed::Box; -use collections::{BTreeMap, Vec, VecDeque}; +use collections::{BTreeMap, Vec}; use spin::Mutex; use arch; +use context::file::File; +use context::memory::{Grant, Memory, SharedMemory}; use syscall::data::Event; -use super::file::File; -use super::memory::{Grant, Memory, SharedMemory}; +use sync::{WaitCondition, WaitQueue}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Status { @@ -36,6 +37,10 @@ pub struct Context { pub running: bool, /// Context is halting parent pub vfork: bool, + /// Context is being waited on + pub waitpid: Arc, + /// Context should wake up at specified time + pub wake: Option<(u64, u64)>, /// The architecture specific context pub arch: arch::context::Context, /// Kernel FX @@ -53,7 +58,7 @@ pub struct Context { /// The current working directory pub cwd: Arc>>, /// Kernel events - pub events: Arc>>, + pub events: Arc>, /// The process environment pub env: Arc, Arc>>>>>, /// The open files in the scheme @@ -73,6 +78,8 @@ impl Context { status: Status::Blocked, running: false, vfork: false, + waitpid: Arc::new(WaitCondition::new()), + wake: None, arch: arch::context::Context::new(), kfx: None, kstack: None, @@ -81,7 +88,7 @@ impl Context { stack: None, grants: Arc::new(Mutex::new(Vec::new())), cwd: Arc::new(Mutex::new(Vec::new())), - events: Arc::new(Mutex::new(VecDeque::new())), + events: Arc::new(WaitQueue::new()), env: Arc::new(Mutex::new(BTreeMap::new())), files: Arc::new(Mutex::new(Vec::new())) } @@ -128,6 +135,24 @@ impl Context { } } + pub fn block(&mut self) -> bool { + if self.status == Status::Runnable { + self.status = Status::Blocked; + true + } else { + false + } + } + + pub fn unblock(&mut self) -> bool { + if self.status == Status::Blocked { + self.status = Status::Runnable; + true + } else { + false + } + } + /// Add a file to the lowest available slot. /// Return the file descriptor number or None if no slot was found pub fn add_file(&self, file: File) -> Option { diff --git a/kernel/context/event.rs b/kernel/context/event.rs index 06fea60..19d119d 100644 --- a/kernel/context/event.rs +++ b/kernel/context/event.rs @@ -1,11 +1,12 @@ use alloc::arc::{Arc, Weak}; -use collections::{BTreeMap, VecDeque}; -use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use collections::BTreeMap; +use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; use context; +use sync::WaitQueue; use syscall::data::Event; -type EventList = Weak>>; +type EventList = Weak>; type Registry = BTreeMap<(usize, usize), BTreeMap<(usize, usize), EventList>>; @@ -67,9 +68,8 @@ pub fn trigger(scheme_id: usize, id: usize, flags: usize, data: usize) { let registry = registry(); if let Some(event_lists) = registry.get(&(scheme_id, id)) { for entry in event_lists.iter() { - if let Some(event_list_lock) = entry.1.upgrade() { - let mut event_list = event_list_lock.lock(); - event_list.push_back(Event { + if let Some(event_list) = entry.1.upgrade() { + event_list.send(Event { id: (entry.0).1, flags: flags, data: data diff --git a/kernel/context/switch.rs b/kernel/context/switch.rs index 9065530..0e291e6 100644 --- a/kernel/context/switch.rs +++ b/kernel/context/switch.rs @@ -16,36 +16,55 @@ pub unsafe fn switch() -> bool { arch::interrupt::pause(); } - let from_ptr = { - let contexts = contexts(); - let context_lock = contexts.current().expect("context::switch: Not inside of context"); - let mut context = context_lock.write(); - context.deref_mut() as *mut Context - }; - + let from_ptr; let mut to_ptr = 0 as *mut Context; - - for (pid, context_lock) in contexts().iter() { - if *pid > (*from_ptr).id { + { + let contexts = contexts(); + { + let context_lock = contexts.current().expect("context::switch: Not inside of context"); let mut context = context_lock.write(); - if context.status == Status::Runnable && ! context.running { - to_ptr = context.deref_mut() as *mut Context; - break; - } + from_ptr = context.deref_mut() as *mut Context; } - } - if to_ptr as usize == 0 { - for (pid, context_lock) in contexts().iter() { - if *pid < (*from_ptr).id { + let check_context = |context: &mut Context| -> bool { + if context.status == Status::Blocked && context.wake.is_some() { + let wake = context.wake.expect("context::switch: wake not set"); + + let current = arch::time::monotonic(); + if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) { + context.unblock(); + } + } + + if context.status == Status::Runnable && ! context.running { + true + } else { + false + } + }; + + for (pid, context_lock) in contexts.iter() { + if *pid > (*from_ptr).id { let mut context = context_lock.write(); - if context.status == Status::Runnable && ! context.running { + if check_context(&mut context) { to_ptr = context.deref_mut() as *mut Context; break; } } } - } + + if to_ptr as usize == 0 { + for (pid, context_lock) in contexts.iter() { + if *pid < (*from_ptr).id { + let mut context = context_lock.write(); + if check_context(&mut context) { + to_ptr = context.deref_mut() as *mut Context; + break; + } + } + } + } + }; if to_ptr as usize == 0 { // Unset global lock if no context found diff --git a/kernel/lib.rs b/kernel/lib.rs index 3119c25..d71af53 100644 --- a/kernel/lib.rs +++ b/kernel/lib.rs @@ -69,6 +69,7 @@ #![feature(asm)] #![feature(collections)] #![feature(const_fn)] +#![feature(core_intrinsics)] #![feature(drop_types_in_const)] #![feature(heap_api)] #![feature(integer_atomics)] @@ -115,6 +116,9 @@ pub mod elf; /// Schemes, filesystem handlers pub mod scheme; +/// Synchronization primitives +pub mod sync; + /// Syscall handlers pub mod syscall; diff --git a/kernel/scheme/debug.rs b/kernel/scheme/debug.rs index 52d9a8b..e082ac6 100644 --- a/kernel/scheme/debug.rs +++ b/kernel/scheme/debug.rs @@ -1,31 +1,27 @@ -use collections::VecDeque; use core::str; use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; -use spin::{Mutex, Once}; +use spin::Once; use context; +use sync::WaitQueue; use syscall::error::*; use syscall::flag::EVENT_READ; use syscall::scheme::Scheme; pub static DEBUG_SCHEME_ID: AtomicUsize = ATOMIC_USIZE_INIT; -/// Input -static INPUT: Once>> = Once::new(); +/// Input queue +static INPUT: Once> = Once::new(); -/// Initialize contexts, called if needed -fn init_input() -> Mutex> { - Mutex::new(VecDeque::new()) +/// Initialize input queue, called if needed +fn init_input() -> WaitQueue { + WaitQueue::new() } -/// Get the global schemes list, const +/// Add to the input queue #[no_mangle] pub extern fn debug_input(b: u8) { - let len = { - let mut input = INPUT.call_once(init_input).lock(); - input.push_back(b); - input.len() - }; + let len = INPUT.call_once(init_input).send(b); context::event::trigger(DEBUG_SCHEME_ID.load(Ordering::SeqCst), 0, EVENT_READ, len); } @@ -45,22 +41,7 @@ impl Scheme for DebugScheme { /// /// Returns the number of bytes read fn read(&self, _file: usize, buf: &mut [u8]) -> Result { - loop { - let mut i = 0; - { - let mut input = INPUT.call_once(init_input).lock(); - while i < buf.len() && ! input.is_empty() { - buf[i] = input.pop_front().expect("debug_input lost byte"); - i += 1; - } - } - - if i > 0 { - return Ok(i); - } else { - unsafe { context::switch(); } //TODO: Block - } - } + Ok(INPUT.call_once(init_input).receive_into(buf)) } /// Write the `buffer` to the `file` diff --git a/kernel/scheme/event.rs b/kernel/scheme/event.rs index 8229559..1634e1b 100644 --- a/kernel/scheme/event.rs +++ b/kernel/scheme/event.rs @@ -1,17 +1,18 @@ use alloc::arc::{Arc, Weak}; -use collections::{BTreeMap, VecDeque}; -use core::mem; +use collections::BTreeMap; +use core::{mem, slice}; use core::sync::atomic::{AtomicUsize, Ordering}; -use spin::{Mutex, RwLock}; +use spin::RwLock; use context; +use sync::WaitQueue; use syscall::data::Event; use syscall::error::*; use syscall::scheme::Scheme; pub struct EventScheme { next_id: AtomicUsize, - handles: RwLock>>>> + handles: RwLock>>> } impl EventScheme { @@ -57,29 +58,8 @@ impl Scheme for EventScheme { handle_weak.upgrade().ok_or(Error::new(EBADF))? }; - let event_size = mem::size_of::(); - let len = buf.len()/event_size; - if len > 0 { - loop { - let mut i = 0; - { - let mut events = handle.lock(); - while ! events.is_empty() && i < len { - let event = events.pop_front().unwrap(); - unsafe { *(buf.as_mut_ptr() as *mut Event).offset(i as isize) = event; } - i += 1; - } - } - - if i > 0 { - return Ok(i * event_size); - } else { - unsafe { context::switch(); } //TODO: Block - } - } - } else { - Ok(0) - } + let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::()) }; + Ok(handle.receive_into(event_buf) * mem::size_of::()) } fn fsync(&self, id: usize) -> Result { diff --git a/kernel/scheme/irq.rs b/kernel/scheme/irq.rs index 62fcb1a..2ed891e 100644 --- a/kernel/scheme/irq.rs +++ b/kernel/scheme/irq.rs @@ -1,21 +1,54 @@ use core::{mem, str}; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use spin::{Mutex, Once}; -use arch::interrupt::irq::{ACKS, COUNTS, acknowledge}; +use arch::interrupt::irq::acknowledge; +use context; +use sync::WaitCondition; use syscall::error::*; +use syscall::flag::EVENT_READ; use syscall::scheme::Scheme; +pub static IRQ_SCHEME_ID: AtomicUsize = ATOMIC_USIZE_INIT; + +/// IRQ queues +static ACKS: Mutex<[usize; 16]> = Mutex::new([0; 16]); +static COUNTS: Mutex<[usize; 16]> = Mutex::new([0; 16]); +static WAITS: Once<[WaitCondition; 16]> = Once::new(); + +fn init_waits() -> [WaitCondition; 16] { + [ + WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), + WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), + WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), + WaitCondition::new(), WaitCondition::new(), WaitCondition::new(), WaitCondition::new() + ] +} + +/// Add to the input queue +#[no_mangle] +pub extern fn irq_trigger(irq: u8) { + COUNTS.lock()[irq as usize] += 1; + WAITS.call_once(init_waits)[irq as usize].notify(); + context::event::trigger(IRQ_SCHEME_ID.load(Ordering::SeqCst), irq as usize, EVENT_READ, mem::size_of::()); +} + pub struct IrqScheme; impl Scheme for IrqScheme { - fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { - let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; + fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { + if uid == 0 { + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?; - let id = path_str.parse::().or(Err(Error::new(ENOENT)))?; + let id = path_str.parse::().or(Err(Error::new(ENOENT)))?; - if id < COUNTS.lock().len() { - Ok(id) + if id < COUNTS.lock().len() { + Ok(id) + } else { + Err(Error::new(ENOENT)) + } } else { - Err(Error::new(ENOENT)) + Err(Error::new(EACCES)) } } @@ -26,15 +59,17 @@ impl Scheme for IrqScheme { fn read(&self, file: usize, buffer: &mut [u8]) -> Result { // Ensures that the length of the buffer is larger than the size of a usize if buffer.len() >= mem::size_of::() { - let ack = ACKS.lock()[file]; - let current = COUNTS.lock()[file]; - if ack != current { - // Safe if the length of the buffer is larger than the size of a usize - assert!(buffer.len() >= mem::size_of::()); - unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; } - Ok(mem::size_of::()) - } else { - Ok(0) + loop { + let ack = ACKS.lock()[file]; + let current = COUNTS.lock()[file]; + if ack != current { + // Safe if the length of the buffer is larger than the size of a usize + assert!(buffer.len() >= mem::size_of::()); + unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; } + return Ok(mem::size_of::()); + } else { + WAITS.call_once(init_waits)[file].wait(); + } } } else { Err(Error::new(EINVAL)) @@ -58,6 +93,10 @@ impl Scheme for IrqScheme { } } + fn fevent(&self, _file: usize, _flags: usize) -> Result { + Ok(0) + } + fn fsync(&self, _file: usize) -> Result { Ok(0) } diff --git a/kernel/scheme/mod.rs b/kernel/scheme/mod.rs index d64d03f..1d61491 100644 --- a/kernel/scheme/mod.rs +++ b/kernel/scheme/mod.rs @@ -19,9 +19,9 @@ use self::debug::{DEBUG_SCHEME_ID, DebugScheme}; use self::event::EventScheme; use self::env::EnvScheme; use self::initfs::InitFsScheme; -use self::irq::IrqScheme; +use self::irq::{IRQ_SCHEME_ID, IrqScheme}; use self::pipe::{PIPE_SCHEME_ID, PipeScheme}; -use self::root::RootScheme; +use self::root::{ROOT_SCHEME_ID, RootScheme}; /// Debug scheme pub mod debug; @@ -114,12 +114,12 @@ static SCHEMES: Once> = Once::new(); /// Initialize schemes, called if needed fn init_schemes() -> RwLock { let mut list: SchemeList = SchemeList::new(); - list.insert(Box::new(*b""), Arc::new(Box::new(RootScheme::new()))).expect("failed to insert root scheme"); + ROOT_SCHEME_ID.store(list.insert(Box::new(*b""), Arc::new(Box::new(RootScheme::new()))).expect("failed to insert root scheme"), Ordering::SeqCst); DEBUG_SCHEME_ID.store(list.insert(Box::new(*b"debug"), Arc::new(Box::new(DebugScheme))).expect("failed to insert debug scheme"), Ordering::SeqCst); list.insert(Box::new(*b"event"), Arc::new(Box::new(EventScheme::new()))).expect("failed to insert event scheme"); list.insert(Box::new(*b"env"), Arc::new(Box::new(EnvScheme::new()))).expect("failed to insert env scheme"); list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme"); - list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme"); + IRQ_SCHEME_ID.store(list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme"), Ordering::SeqCst); PIPE_SCHEME_ID.store(list.insert(Box::new(*b"pipe"), Arc::new(Box::new(PipeScheme))).expect("failed to insert pipe scheme"), Ordering::SeqCst); RwLock::new(list) } diff --git a/kernel/scheme/pipe.rs b/kernel/scheme/pipe.rs index f9e680d..a17be8f 100644 --- a/kernel/scheme/pipe.rs +++ b/kernel/scheme/pipe.rs @@ -1,8 +1,9 @@ use alloc::arc::{Arc, Weak}; -use collections::{BTreeMap, VecDeque}; +use collections::BTreeMap; use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; -use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use sync::WaitQueue; use syscall::error::{Error, Result, EBADF, EPIPE}; use syscall::scheme::Scheme; @@ -103,43 +104,21 @@ impl Scheme for PipeScheme { /// Read side of a pipe #[derive(Clone)] pub struct PipeRead { - vec: Arc>> + vec: Arc> } impl PipeRead { pub fn new() -> Self { PipeRead { - vec: Arc::new(Mutex::new(VecDeque::new())) + vec: Arc::new(WaitQueue::new()) } } fn read(&self, buf: &mut [u8]) -> Result { - if buf.is_empty() || (Arc::weak_count(&self.vec) == 0 && self.vec.lock().is_empty()) { + if buf.is_empty() || (Arc::weak_count(&self.vec) == 0 && self.vec.is_empty()) { Ok(0) } else { - /*loop { - { - if let Some(byte) = self.vec.lock().pop_front() { - buf[0] = byte; - break; - } - } - unsafe { context::switch(); } - }*/ - - let mut i = 0; - - while i < buf.len() { - match self.vec.lock().pop_front() { - Some(b) => { - buf[i] = b; - i += 1; - }, - None => break - } - } - - Ok(i) + Ok(self.vec.receive_into(buf)) } } } @@ -147,7 +126,7 @@ impl PipeRead { /// Read side of a pipe #[derive(Clone)] pub struct PipeWrite { - vec: Weak>>, + vec: Weak>, } impl PipeWrite { @@ -160,9 +139,7 @@ impl PipeWrite { fn write(&self, buf: &[u8]) -> Result { match self.vec.upgrade() { Some(vec) => { - for &b in buf.iter() { - vec.lock().push_back(b); - } + vec.send_from(buf); Ok(buf.len()) }, diff --git a/kernel/scheme/root.rs b/kernel/scheme/root.rs index c088d6e..1d4d4d0 100644 --- a/kernel/scheme/root.rs +++ b/kernel/scheme/root.rs @@ -1,7 +1,7 @@ use alloc::arc::Arc; use alloc::boxed::Box; use collections::BTreeMap; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use spin::RwLock; use context; @@ -10,6 +10,8 @@ use syscall::scheme::Scheme; use scheme; use scheme::user::{UserInner, UserScheme}; +pub static ROOT_SCHEME_ID: AtomicUsize = ATOMIC_USIZE_INIT; + pub struct RootScheme { next_id: AtomicUsize, handles: RwLock>> @@ -25,28 +27,33 @@ impl RootScheme { } impl Scheme for RootScheme { - fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { - let context = { - let contexts = context::contexts(); - let context = contexts.current().ok_or(Error::new(ESRCH))?; - Arc::downgrade(&context) - }; + fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { + if uid == 0 { + let context = { + let contexts = context::contexts(); + let context = contexts.current().ok_or(Error::new(ESRCH))?; + Arc::downgrade(&context) + }; - let inner = { - let mut schemes = scheme::schemes_mut(); - if schemes.get_name(path).is_some() { - return Err(Error::new(EEXIST)); - } - let inner = Arc::new(UserInner::new(context)); - let id = schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme"); - inner.scheme_id.store(id, Ordering::SeqCst); - inner - }; + let id = self.next_id.fetch_add(1, Ordering::SeqCst); - let id = self.next_id.fetch_add(1, Ordering::SeqCst); - self.handles.write().insert(id, inner); + let inner = { + let mut schemes = scheme::schemes_mut(); + if schemes.get_name(path).is_some() { + return Err(Error::new(EEXIST)); + } + let inner = Arc::new(UserInner::new(id, context)); + let scheme_id = schemes.insert(path.to_vec().into_boxed_slice(), Arc::new(Box::new(UserScheme::new(Arc::downgrade(&inner))))).expect("failed to insert user scheme"); + inner.scheme_id.store(scheme_id, Ordering::SeqCst); + inner + }; - Ok(id) + self.handles.write().insert(id, inner); + + Ok(id) + } else { + Err(Error::new(EACCES)) + } } fn dup(&self, file: usize) -> Result { @@ -82,6 +89,10 @@ impl Scheme for RootScheme { inner.write(buf) } + fn fevent(&self, _file: usize, _flags: usize) -> Result { + Ok(0) + } + fn fsync(&self, _file: usize) -> Result { Ok(0) } diff --git a/kernel/scheme/user.rs b/kernel/scheme/user.rs index 6bc25fd..fbd2726 100644 --- a/kernel/scheme/user.rs +++ b/kernel/scheme/user.rs @@ -1,35 +1,39 @@ use alloc::arc::Weak; -use collections::{BTreeMap, VecDeque}; use core::sync::atomic::{AtomicUsize, AtomicU64, Ordering}; -use core::{mem, usize}; -use spin::{Mutex, RwLock}; +use core::{mem, slice, usize}; +use spin::RwLock; use arch; use arch::paging::{InactivePageTable, Page, VirtualAddress, entry}; use arch::paging::temporary_page::TemporaryPage; use context::{self, Context}; use context::memory::Grant; +use scheme::root::ROOT_SCHEME_ID; +use sync::{WaitQueue, WaitMap}; use syscall::data::{Packet, Stat}; use syscall::error::*; +use syscall::flag::EVENT_READ; use syscall::number::*; use syscall::scheme::Scheme; pub struct UserInner { + handle_id: usize, pub scheme_id: AtomicUsize, next_id: AtomicU64, context: Weak>, - todo: Mutex>, - done: Mutex> + todo: WaitQueue, + done: WaitMap } impl UserInner { - pub fn new(context: Weak>) -> UserInner { + pub fn new(handle_id: usize, context: Weak>) -> UserInner { UserInner { + handle_id: handle_id, scheme_id: AtomicUsize::new(0), next_id: AtomicU64::new(1), context: context, - todo: Mutex::new(VecDeque::new()), - done: Mutex::new(BTreeMap::new()) + todo: WaitQueue::new(), + done: WaitMap::new() } } @@ -54,18 +58,10 @@ impl UserInner { d: d }; - self.todo.lock().push_back(packet); + let len = self.todo.send(packet); + context::event::trigger(ROOT_SCHEME_ID.load(Ordering::SeqCst), self.handle_id, EVENT_READ, len * mem::size_of::()); - loop { - { - let mut done = self.done.lock(); - if let Some(a) = done.remove(&id) { - return Error::demux(a); - } - } - - unsafe { context::switch(); } //TODO: Block - } + Error::demux(self.done.receive(&id)) } pub fn capture(&self, buf: &[u8]) -> Result { @@ -158,29 +154,8 @@ impl UserInner { } pub fn read(&self, buf: &mut [u8]) -> Result { - let packet_size = mem::size_of::(); - let len = buf.len()/packet_size; - if len > 0 { - loop { - let mut i = 0; - { - let mut todo = self.todo.lock(); - while ! todo.is_empty() && i < len { - let packet = todo.pop_front().unwrap(); - unsafe { *(buf.as_mut_ptr() as *mut Packet).offset(i as isize) = packet; } - i += 1; - } - } - - if i > 0 { - return Ok(i * packet_size); - } else { - unsafe { context::switch(); } //TODO: Block - } - } - } else { - Ok(0) - } + let packet_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Packet, buf.len()/mem::size_of::()) }; + Ok(self.todo.receive_into(packet_buf) * mem::size_of::()) } pub fn write(&self, buf: &[u8]) -> Result { @@ -195,7 +170,7 @@ impl UserInner { _ => println!("Unknown scheme -> kernel message {}", packet.a) } } else { - self.done.lock().insert(packet.id, packet.a); + self.done.send(packet.id, packet.a); } i += 1; } diff --git a/kernel/sync/mod.rs b/kernel/sync/mod.rs new file mode 100644 index 0000000..833925b --- /dev/null +++ b/kernel/sync/mod.rs @@ -0,0 +1,7 @@ +pub use self::wait_condition::WaitCondition; +pub use self::wait_queue::WaitQueue; +pub use self::wait_map::WaitMap; + +pub mod wait_condition; +pub mod wait_queue; +pub mod wait_map; diff --git a/kernel/sync/wait_condition.rs b/kernel/sync/wait_condition.rs new file mode 100644 index 0000000..997b458 --- /dev/null +++ b/kernel/sync/wait_condition.rs @@ -0,0 +1,49 @@ +use alloc::arc::Arc; +use collections::Vec; +use core::mem; +use spin::{Mutex, RwLock}; + +use context::{self, Context}; + +#[derive(Debug)] +pub struct WaitCondition { + contexts: Mutex>>> +} + +impl WaitCondition { + pub fn new() -> WaitCondition { + WaitCondition { + contexts: Mutex::new(Vec::new()) + } + } + + pub fn notify(&self) -> usize { + let mut contexts = Vec::new(); + mem::swap(&mut *self.contexts.lock(), &mut contexts); + for context_lock in contexts.iter() { + context_lock.write().unblock(); + } + contexts.len() + } + + pub fn wait(&self) { + { + let context_lock = { + let contexts = context::contexts(); + let context_lock = contexts.current().expect("WaitCondition::wait: no context"); + context_lock.clone() + }; + + context_lock.write().block(); + + self.contexts.lock().push(context_lock); + } + unsafe { context::switch(); } + } +} + +impl Drop for WaitCondition { + fn drop(&mut self){ + self.notify(); + } +} diff --git a/kernel/sync/wait_map.rs b/kernel/sync/wait_map.rs new file mode 100644 index 0000000..468ad8c --- /dev/null +++ b/kernel/sync/wait_map.rs @@ -0,0 +1,33 @@ +use collections::BTreeMap; +use spin::Mutex; + +use sync::WaitCondition; + +#[derive(Debug)] +pub struct WaitMap { + inner: Mutex>, + condition: WaitCondition +} + +impl WaitMap where K: Ord { + pub fn new() -> WaitMap { + WaitMap { + inner: Mutex::new(BTreeMap::new()), + condition: WaitCondition::new() + } + } + + pub fn send(&self, key: K, value: V) { + self.inner.lock().insert(key, value); + self.condition.notify(); + } + + pub fn receive(&self, key: &K) -> V { + loop { + if let Some(value) = self.inner.lock().remove(key) { + return value; + } + self.condition.wait(); + } + } +} diff --git a/kernel/sync/wait_queue.rs b/kernel/sync/wait_queue.rs new file mode 100644 index 0000000..445164c --- /dev/null +++ b/kernel/sync/wait_queue.rs @@ -0,0 +1,95 @@ +use collections::vec_deque::VecDeque; +use core::mem; +use core::ops::DerefMut; +use spin::Mutex; + +use sync::WaitCondition; + +#[derive(Debug)] +pub struct WaitQueue { + pub inner: Mutex>, + pub condition: WaitCondition, +} + +impl WaitQueue { + pub fn new() -> WaitQueue { + WaitQueue { + inner: Mutex::new(VecDeque::new()), + condition: WaitCondition::new() + } + } + + pub fn clone(&self) -> WaitQueue where T: Clone { + WaitQueue { + inner: Mutex::new(self.inner.lock().clone()), + condition: WaitCondition::new() + } + } + + pub fn is_empty(&self) -> bool { + self.inner.lock().is_empty() + } + + pub fn receive(&self) -> T { + loop { + if let Some(value) = self.inner.lock().pop_front() { + return value; + } + self.condition.wait(); + } + } + + pub fn receive_into(&self, buf: &mut [T]) -> usize { + let mut i = 0; + + if i < buf.len() { + buf[i] = self.receive(); + i += 1; + } + + while i < buf.len() { + if let Some(value) = self.inner.lock().pop_front() { + buf[i] = value; + i += 1; + } else { + break; + } + } + + i + } + + pub fn receive_all(&self) -> VecDeque { + loop { + { + let mut inner = self.inner.lock(); + if ! inner.is_empty() { + let mut swap_inner = VecDeque::new(); + mem::swap(inner.deref_mut(), &mut swap_inner); + return swap_inner; + } + } + self.condition.wait(); + } + } + + pub fn send(&self, value: T) -> usize { + let len = { + let mut inner = self.inner.lock(); + inner.push_back(value); + inner.len() + }; + self.condition.notify(); + len + } + + pub fn send_from(&self, buf: &[T]) -> usize where T: Copy { + let len = { + let mut inner = self.inner.lock(); + inner.extend(buf.iter()); + inner.len() + }; + self.condition.notify(); + len + } +} diff --git a/kernel/syscall/futex.rs b/kernel/syscall/futex.rs new file mode 100644 index 0000000..4b9c30c --- /dev/null +++ b/kernel/syscall/futex.rs @@ -0,0 +1,110 @@ +use alloc::arc::Arc; +use collections::VecDeque; +use core::intrinsics; +use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +use context::{self, Context}; +use syscall::error::{Error, Result, ESRCH, EAGAIN, EINVAL}; +use syscall::flag::{FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; +use syscall::validate::validate_slice_mut; + +type FutexList = VecDeque<(usize, Arc>)>; + +/// Fast userspace mutex list +static FUTEXES: Once> = Once::new(); + +/// Initialize futexes, called if needed +fn init_futexes() -> RwLock { + RwLock::new(VecDeque::new()) +} + +/// Get the global futexes list, const +pub fn futexes() -> RwLockReadGuard<'static, FutexList> { + FUTEXES.call_once(init_futexes).read() +} + +/// Get the global futexes list, mutable +pub fn futexes_mut() -> RwLockWriteGuard<'static, FutexList> { + FUTEXES.call_once(init_futexes).write() +} + +pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) -> Result { + match op { + FUTEX_WAIT => { + { + let mut futexes = futexes_mut(); + + let context_lock = { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + context_lock.clone() + }; + + if unsafe { intrinsics::atomic_load(addr) != val } { + return Err(Error::new(EAGAIN)); + } + + context_lock.write().block(); + + futexes.push_back((addr as *mut i32 as usize, context_lock)); + } + + unsafe { context::switch(); } + + Ok(0) + }, + FUTEX_WAKE => { + let mut woken = 0; + + { + let mut futexes = futexes_mut(); + + let mut i = 0; + while i < futexes.len() && (woken as i32) < val { + if futexes[i].0 == addr as *mut i32 as usize { + if let Some(futex) = futexes.swap_remove_back(i) { + futex.1.write().unblock(); + woken += 1; + } + } else { + i += 1; + } + } + } + + Ok(woken) + }, + FUTEX_REQUEUE => { + let addr2_safe = validate_slice_mut(addr2, 1).map(|addr2_safe| &mut addr2_safe[0])?; + + let mut woken = 0; + let mut requeued = 0; + + { + let mut futexes = futexes_mut(); + + let mut i = 0; + while i < futexes.len() && (woken as i32) < val { + if futexes[i].0 == addr as *mut i32 as usize { + if let Some(futex) = futexes.swap_remove_back(i) { + futex.1.write().unblock(); + woken += 1; + } + } else { + i += 1; + } + } + while i < futexes.len() && requeued < val2 { + if futexes[i].0 == addr as *mut i32 as usize { + futexes[i].0 = addr2_safe as *mut i32 as usize; + requeued += 1; + } + i += 1; + } + } + + Ok(woken) + }, + _ => Err(Error::new(EINVAL)) + } +} diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs index 9de8b92..8e1c190 100644 --- a/kernel/syscall/mod.rs +++ b/kernel/syscall/mod.rs @@ -5,6 +5,7 @@ extern crate syscall; pub use self::syscall::{data, error, flag, number, scheme}; pub use self::fs::*; +pub use self::futex::futex; pub use self::process::*; pub use self::time::*; pub use self::validate::*; @@ -16,6 +17,9 @@ use self::number::*; /// Filesystem syscalls pub mod fs; +/// Fast userspace mutex +pub mod futex; + /// Process syscalls pub mod process; @@ -28,7 +32,7 @@ pub mod validate; #[no_mangle] pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize { #[inline(always)] - fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, _f: usize, stack: usize) -> Result { + fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> Result { match a & SYS_CLASS { SYS_CLASS_FILE => match a & SYS_ARG { SYS_ARG_SLICE => file_op_slice(a, b, validate_slice(c as *const u8, d)?), @@ -66,6 +70,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize SYS_SETUID => setuid(b as u32), SYS_SETGID => setgid(b as u32), SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?), + SYS_FUTEX => futex(validate_slice_mut(b as *mut i32, 1).map(|uaddr| &mut uaddr[0])?, c, d as i32, e, f as *mut i32), SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c), SYS_PHYSALLOC => physalloc(b), SYS_PHYSFREE => physfree(b, c), @@ -78,10 +83,10 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize } let result = inner(a, b, c, d, e, f, stack); - +/* if let Err(ref err) = result { - println!("{}, {}, {}, {}: {}", a & 0xFFFF, b, c, d, err); + println!("{}, {}, {}, {}: {}", a, b, c, d, err); } - +*/ Error::mux(result) } diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index cdd768f..765edb5 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -2,9 +2,8 @@ use alloc::arc::Arc; use alloc::boxed::Box; use collections::{BTreeMap, Vec}; -use core::mem; +use core::{mem, str}; use core::ops::DerefMut; -use core::str; use spin::Mutex; use arch; @@ -247,7 +246,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let mut context = context_lock.write(); - context.status = context::Status::Blocked; + context.block(); vfork = true; } else { vfork = false; @@ -416,14 +415,13 @@ pub fn exit(status: usize) -> ! { let vfork = context.vfork; context.vfork = false; + context.waitpid.notify(); (vfork, context.ppid) }; if vfork { if let Some(context_lock) = contexts.get(ppid) { let mut context = context_lock.write(); - if context.status == context::Status::Blocked { - context.status = context::Status::Runnable; - } else { + if ! context.unblock() { println!("{} not blocked for exit vfork unblock", ppid); } } else { @@ -622,9 +620,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { if vfork { if let Some(context_lock) = contexts.get(ppid) { let mut context = context_lock.write(); - if context.status == context::Status::Blocked { - context.status = context::Status::Runnable; - } else { + if ! context.unblock() { println!("{} not blocked for exec vfork unblock", ppid); } } else { @@ -808,7 +804,7 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { loop { { let mut exited = false; - + let waitpid; { let contexts = context::contexts(); let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; @@ -820,6 +816,7 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { } exited = true; } + waitpid = context.waitpid.clone(); } if exited { @@ -827,6 +824,8 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid)); } else if flags & WNOHANG == WNOHANG { return Ok(0); + } else { + waitpid.wait(); } } diff --git a/kernel/syscall/time.rs b/kernel/syscall/time.rs index ca05773..448f311 100644 --- a/kernel/syscall/time.rs +++ b/kernel/syscall/time.rs @@ -26,17 +26,20 @@ pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result end.0 || (current.0 == end.0 && current.1 >= end.1) { - break; - } + { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let mut context = context_lock.write(); + + context.wake = Some(end); + context.block(); } + unsafe { context::switch(); } + if let Some(mut rem) = rem_opt { + //TODO let current = arch::time::monotonic(); rem.tv_sec = 0; rem.tv_nsec = 0; } diff --git a/libstd b/libstd index 07d687d..89b6a73 160000 --- a/libstd +++ b/libstd @@ -1 +1 @@ -Subproject commit 07d687dedcb9a8d76830fd54f6b992cbc3e49aa4 +Subproject commit 89b6a739be426e6ff6e401f6495e7022a90f4d42 diff --git a/programs/netutils b/programs/netutils new file mode 160000 index 0000000..25c1366 --- /dev/null +++ b/programs/netutils @@ -0,0 +1 @@ +Subproject commit 25c136612217ade85a0ca64ddfe3f95e5086b593 diff --git a/programs/orbutils b/programs/orbutils new file mode 160000 index 0000000..7e7d2ad --- /dev/null +++ b/programs/orbutils @@ -0,0 +1 @@ +Subproject commit 7e7d2ada724c48b01a69ba72b105392557fa634c diff --git a/schemes/ethernetd/Cargo.toml b/schemes/ethernetd/Cargo.toml new file mode 100644 index 0000000..be6bd95 --- /dev/null +++ b/schemes/ethernetd/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "ethernetd" +version = "0.1.0" + +[dependencies] +resource_scheme = { path = "../../crates/resource_scheme/" } +syscall = { path = "../../syscall/" } diff --git a/schemes/ethernetd/src/common.rs b/schemes/ethernetd/src/common.rs new file mode 100644 index 0000000..12ebd5a --- /dev/null +++ b/schemes/ethernetd/src/common.rs @@ -0,0 +1,105 @@ +use std::{mem, slice, u8, u16}; + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +#[repr(packed)] +pub struct n16(u16); + +impl n16 { + pub fn new(value: u16) -> Self { + n16(value.to_be()) + } + + pub fn get(&self) -> u16 { + u16::from_be(self.0) + } + + pub fn set(&mut self, value: u16) { + self.0 = value.to_be(); + } +} + +#[derive(Copy, Clone)] +pub struct MacAddr { + pub bytes: [u8; 6], +} + +impl MacAddr { + pub fn equals(&self, other: Self) -> bool { + for i in 0..6 { + if self.bytes[i] != other.bytes[i] { + return false; + } + } + true + } + + pub fn from_str(string: &str) -> Self { + let mut addr = MacAddr { bytes: [0, 0, 0, 0, 0, 0] }; + + let mut i = 0; + for part in string.split('.') { + let octet = u8::from_str_radix(part, 16).unwrap_or(0); + match i { + 0 => addr.bytes[0] = octet, + 1 => addr.bytes[1] = octet, + 2 => addr.bytes[2] = octet, + 3 => addr.bytes[3] = octet, + 4 => addr.bytes[4] = octet, + 5 => addr.bytes[5] = octet, + _ => break, + } + i += 1; + } + + addr + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + for i in 0..6 { + if i > 0 { + string.push('.'); + } + string.push_str(&format!("{:X}", self.bytes[i])); + } + string + } +} + +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct EthernetIIHeader { + pub dst: MacAddr, + pub src: MacAddr, + pub ethertype: n16, +} + +pub struct EthernetII { + pub header: EthernetIIHeader, + pub data: Vec, +} + +impl EthernetII { + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() >= mem::size_of::() { + unsafe { + return Some(EthernetII { + header: *(bytes.as_ptr() as *const EthernetIIHeader), + data: bytes[mem::size_of::() ..].to_vec(), + }); + } + } + None + } + + pub fn to_bytes(&self) -> Vec { + unsafe { + let header_ptr: *const EthernetIIHeader = &self.header; + let mut ret = Vec::from(slice::from_raw_parts(header_ptr as *const u8, + mem::size_of::())); + ret.extend_from_slice(&self.data); + ret + } + } +} diff --git a/schemes/ethernetd/src/main.rs b/schemes/ethernetd/src/main.rs new file mode 100644 index 0000000..23d6c55 --- /dev/null +++ b/schemes/ethernetd/src/main.rs @@ -0,0 +1,28 @@ +extern crate resource_scheme; +extern crate syscall; + +use std::fs::File; +use std::io::{Read, Write}; +use std::thread; + +use resource_scheme::ResourceScheme; +use syscall::Packet; + +use scheme::EthernetScheme; + +pub mod common; +pub mod resource; +pub mod scheme; + +fn main() { + thread::spawn(move || { + let mut socket = File::create(":ethernet").expect("ethernetd: failed to create ethernet scheme"); + let scheme = EthernetScheme; + loop { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("ethernetd: failed to read events from ethernet scheme"); + scheme.handle(&mut packet); + socket.write(&packet).expect("ethernetd: failed to write responses to ethernet scheme"); + } + }); +} diff --git a/schemes/ethernetd/src/resource.rs b/schemes/ethernetd/src/resource.rs new file mode 100644 index 0000000..d991aa5 --- /dev/null +++ b/schemes/ethernetd/src/resource.rs @@ -0,0 +1,97 @@ +use std::{cmp, mem}; + +use resource_scheme::Resource; +use syscall; +use syscall::error::*; + +use common::{n16, MacAddr, EthernetIIHeader, EthernetII}; + +/// A ethernet resource +pub struct EthernetResource { + /// The network + pub network: usize, + /// The data + pub data: Vec, + /// The MAC addresss + pub peer_addr: MacAddr, + /// The ethernet type + pub ethertype: u16, +} + +impl Resource for EthernetResource { + fn dup(&self) -> Result> { + let network = try!(syscall::dup(self.network)); + Ok(Box::new(EthernetResource { + network: network, + data: self.data.clone(), + peer_addr: self.peer_addr, + ethertype: self.ethertype, + })) + } + + fn path(&self, buf: &mut [u8]) -> Result { + let path_string = format!("ethernet:{}/{:X}", self.peer_addr.to_string(), self.ethertype); + let path = path_string.as_bytes(); + + for (b, p) in buf.iter_mut().zip(path.iter()) { + *b = *p; + } + + Ok(cmp::min(buf.len(), path.len())) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.data.is_empty() { + let mut data: Vec = Vec::new(); + mem::swap(&mut self.data, &mut data); + + for (b, d) in buf.iter_mut().zip(data.iter()) { + *b = *d; + } + + return Ok(cmp::min(buf.len(), data.len())); + } + + let mut bytes = [0; 65536]; + let count = try!(syscall::read(self.network, &mut bytes)); + + if let Some(frame) = EthernetII::from_bytes(&bytes[..count]) { + if frame.header.ethertype.get() == self.ethertype { + for (b, d) in buf.iter_mut().zip(frame.data.iter()) { + *b = *d; + } + + return Ok(cmp::min(buf.len(), frame.data.len())); + } + } + + Ok(0) + } + + fn write(&mut self, buf: &[u8]) -> Result { + let data = Vec::from(buf); + + match syscall::write(self.network, &EthernetII { + header: EthernetIIHeader { + src: MacAddr { bytes: [0x50, 0x51, 0x52, 0x53, 0x54, 0x55] }, + dst: self.peer_addr, + ethertype: n16::new(self.ethertype), + }, + data: data, + } + .to_bytes()) { + Ok(_) => Ok(buf.len()), + Err(err) => Err(err), + } + } + + fn sync(&mut self) -> Result { + syscall::fsync(self.network) + } +} + +impl Drop for EthernetResource { + fn drop(&mut self) { + let _ = syscall::close(self.network); + } +} diff --git a/schemes/ethernetd/src/scheme.rs b/schemes/ethernetd/src/scheme.rs new file mode 100644 index 0000000..427dc13 --- /dev/null +++ b/schemes/ethernetd/src/scheme.rs @@ -0,0 +1,65 @@ +use std::{str, u16}; + +use resource_scheme::ResourceScheme; +use syscall; +use syscall::error::{Error, Result, EACCES, ENOENT, EINVAL}; +use syscall::flag::O_RDWR; + +use common::{MacAddr, EthernetII}; +use resource::EthernetResource; + +pub struct EthernetScheme; + +impl ResourceScheme for EthernetScheme { + fn open_resource(&self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL)))); + let mut parts = path.split("/"); + if let Some(host_string) = parts.next() { + if let Some(ethertype_string) = parts.next() { + if let Ok(network) = syscall::open("network:", O_RDWR) { + let ethertype = u16::from_str_radix(ethertype_string, 16).unwrap_or(0) as u16; + + if !host_string.is_empty() { + return Ok(Box::new(EthernetResource { + network: network, + data: Vec::new(), + peer_addr: MacAddr::from_str(host_string), + ethertype: ethertype, + })); + } else { + loop { + let mut bytes = [0; 65536]; + match syscall::read(network, &mut bytes) { + Ok(count) => { + if let Some(frame) = EthernetII::from_bytes(&bytes[..count]) { + if frame.header.ethertype.get() == ethertype { + return Ok(Box::new(EthernetResource { + network: network, + data: frame.data, + peer_addr: frame.header.src, + ethertype: ethertype, + })); + } + } + } + Err(_) => break, + } + } + } + } else { + println!("Ethernet: Failed to open network:"); + } + } else { + println!("Ethernet: No ethertype provided"); + } + } else { + println!("Ethernet: No host provided"); + } + + Err(Error::new(ENOENT)) + } else { + Err(Error::new(EACCES)) + } + } +} diff --git a/schemes/ipd/Cargo.toml b/schemes/ipd/Cargo.toml new file mode 100644 index 0000000..26987e8 --- /dev/null +++ b/schemes/ipd/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "ipd" +version = "0.1.0" + +[dependencies] +resource_scheme = { path = "../../crates/resource_scheme/" } +syscall = { path = "../../syscall/" } diff --git a/schemes/ipd/src/common.rs b/schemes/ipd/src/common.rs new file mode 100644 index 0000000..403bcdc --- /dev/null +++ b/schemes/ipd/src/common.rs @@ -0,0 +1,275 @@ +use std::{mem, slice, u8, u16}; + +pub static mut MAC_ADDR: MacAddr = MacAddr { bytes: [0x50, 0x51, 0x52, 0x53, 0x54, 0x55] }; +pub static BROADCAST_MAC_ADDR: MacAddr = MacAddr { bytes: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] }; + +pub static mut IP_ADDR: Ipv4Addr = Ipv4Addr { bytes: [10, 0, 2, 15] }; +pub static mut IP_ROUTER_ADDR: Ipv4Addr = Ipv4Addr { bytes: [10, 0, 2, 2] }; +pub static mut IP_SUBNET: Ipv4Addr = Ipv4Addr { bytes: [255, 255, 255, 0] }; +pub static BROADCAST_IP_ADDR: Ipv4Addr = Ipv4Addr { bytes: [255, 255, 255, 255] }; + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +#[repr(packed)] +pub struct n16(u16); + +impl n16 { + pub fn new(value: u16) -> Self { + n16(value.to_be()) + } + + pub fn get(&self) -> u16 { + u16::from_be(self.0) + } + + pub fn set(&mut self, value: u16) { + self.0 = value.to_be(); + } +} + +#[derive(Copy, Clone)] +pub struct MacAddr { + pub bytes: [u8; 6], +} + +impl MacAddr { + pub fn equals(&self, other: Self) -> bool { + for i in 0..6 { + if self.bytes[i] != other.bytes[i] { + return false; + } + } + true + } + + pub fn from_str(string: &str) -> Self { + let mut addr = MacAddr { bytes: [0, 0, 0, 0, 0, 0] }; + + let mut i = 0; + for part in string.split('.') { + let octet = u8::from_str_radix(part, 16).unwrap_or(0); + match i { + 0 => addr.bytes[0] = octet, + 1 => addr.bytes[1] = octet, + 2 => addr.bytes[2] = octet, + 3 => addr.bytes[3] = octet, + 4 => addr.bytes[4] = octet, + 5 => addr.bytes[5] = octet, + _ => break, + } + i += 1; + } + + addr + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + for i in 0..6 { + if i > 0 { + string.push('.'); + } + string.push_str(&format!("{:X}", self.bytes[i])); + } + string + } +} + +#[derive(Copy, Clone)] +pub struct Ipv4Addr { + pub bytes: [u8; 4], +} + +impl Ipv4Addr { + pub fn equals(&self, other: Self) -> bool { + for i in 0..4 { + if self.bytes[i] != other.bytes[i] { + return false; + } + } + true + } + + pub fn from_str(string: &str) -> Self { + let mut addr = Ipv4Addr { bytes: [0, 0, 0, 0] }; + + let mut i = 0; + for part in string.split('.') { + let octet = part.parse::().unwrap_or(0); + match i { + 0 => addr.bytes[0] = octet, + 1 => addr.bytes[1] = octet, + 2 => addr.bytes[2] = octet, + 3 => addr.bytes[3] = octet, + _ => break, + } + i += 1; + } + + addr + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + + for i in 0..4 { + if i > 0 { + string = string + "."; + } + string = string + &format!("{}", self.bytes[i]); + } + + string + } +} + +#[derive(Copy, Clone)] +pub struct Checksum { + pub data: u16, +} + +impl Checksum { + pub unsafe fn check(&self, mut ptr: usize, mut len: usize) -> bool { + let mut sum: usize = 0; + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + sum == 0xFFFF + } + + pub unsafe fn calculate(&mut self, ptr: usize, len: usize) { + self.data = 0; + + let sum = Checksum::sum(ptr, len); + + self.data = Checksum::compile(sum); + } + + pub unsafe fn sum(mut ptr: usize, mut len: usize) -> usize { + let mut sum = 0; + + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + sum + } + + pub fn compile(mut sum: usize) -> u16 { + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + 0xFFFF - (sum as u16) + } +} + +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct ArpHeader { + pub htype: n16, + pub ptype: n16, + pub hlen: u8, + pub plen: u8, + pub oper: n16, + pub src_mac: MacAddr, + pub src_ip: Ipv4Addr, + pub dst_mac: MacAddr, + pub dst_ip: Ipv4Addr, +} + +pub struct Arp { + pub header: ArpHeader, + pub data: Vec, +} + +impl Arp { + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() >= mem::size_of::() { + unsafe { + return Some(Arp { + header: *(bytes.as_ptr() as *const ArpHeader), + data: bytes[mem::size_of::() ..].to_vec(), + }); + } + } + None + } + + pub fn to_bytes(&self) -> Vec { + unsafe { + let header_ptr: *const ArpHeader = &self.header; + let mut ret = Vec::from(slice::from_raw_parts(header_ptr as *const u8, + mem::size_of::())); + ret.extend_from_slice(&self.data); + ret + } + } +} + +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct Ipv4Header { + pub ver_hlen: u8, + pub services: u8, + pub len: n16, + pub id: n16, + pub flags_fragment: n16, + pub ttl: u8, + pub proto: u8, + pub checksum: Checksum, + pub src: Ipv4Addr, + pub dst: Ipv4Addr, +} + +pub struct Ipv4 { + pub header: Ipv4Header, + pub options: Vec, + pub data: Vec, +} + +impl Ipv4 { + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() >= mem::size_of::() { + unsafe { + let header = *(bytes.as_ptr() as *const Ipv4Header); + let header_len = ((header.ver_hlen & 0xF) << 2) as usize; + + return Some(Ipv4 { + header: header, + options: bytes[mem::size_of::() .. header_len].to_vec(), + data: bytes[header_len .. header.len.get() as usize].to_vec(), + }); + } + } + None + } + + pub fn to_bytes(&self) -> Vec { + unsafe { + let header_ptr: *const Ipv4Header = &self.header; + let mut ret = Vec::::from(slice::from_raw_parts(header_ptr as *const u8, + mem::size_of::())); + ret.extend_from_slice(&self.options); + ret.extend_from_slice(&self.data); + ret + } + } +} diff --git a/schemes/ipd/src/main.rs b/schemes/ipd/src/main.rs new file mode 100644 index 0000000..4ebb15f --- /dev/null +++ b/schemes/ipd/src/main.rs @@ -0,0 +1,30 @@ +#![feature(rand)] + +extern crate resource_scheme; +extern crate syscall; + +use std::fs::File; +use std::io::{Read, Write}; +use std::thread; + +use resource_scheme::ResourceScheme; +use syscall::Packet; + +use scheme::IpScheme; + +pub mod common; +pub mod resource; +pub mod scheme; + +fn main() { + thread::spawn(move || { + let mut socket = File::create(":ip").expect("ipd: failed to create ip scheme"); + let scheme = IpScheme::new(); + loop { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("ipd: failed to read events from ip scheme"); + scheme.handle(&mut packet); + socket.write(&packet).expect("ipd: failed to write responses to ip scheme"); + } + }); +} diff --git a/schemes/ipd/src/resource.rs b/schemes/ipd/src/resource.rs new file mode 100644 index 0000000..d1c4a8d --- /dev/null +++ b/schemes/ipd/src/resource.rs @@ -0,0 +1,114 @@ +use std::{cmp, mem}; + +use resource_scheme::Resource; +use syscall; +use syscall::error::*; + +use common::{n16, Ipv4Addr, Checksum, Ipv4Header, Ipv4, IP_ADDR, BROADCAST_IP_ADDR}; + +/// A IP (internet protocole) resource +pub struct IpResource { + pub link: usize, + pub data: Vec, + pub peer_addr: Ipv4Addr, + pub proto: u8, + pub id: u16, +} + +impl Resource for IpResource { + fn dup(&self) -> Result> { + let link = try!(syscall::dup(self.link)); + Ok(Box::new(IpResource { + link: link, + data: self.data.clone(), + peer_addr: self.peer_addr, + proto: self.proto, + id: self.id, + })) + } + + fn path(&self, buf: &mut [u8]) -> Result { + let path_string = format!("ip:{}/{:X}", self.peer_addr.to_string(), self.proto); + let path = path_string.as_bytes(); + + for (b, p) in buf.iter_mut().zip(path.iter()) { + *b = *p; + } + + Ok(cmp::min(buf.len(), path.len())) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.data.is_empty() { + let mut data: Vec = Vec::new(); + mem::swap(&mut self.data, &mut data); + + for (b, d) in buf.iter_mut().zip(data.iter()) { + *b = *d; + } + + return Ok(cmp::min(buf.len(), data.len())); + } + + let mut bytes = [0; 65536]; + let count = try!(syscall::read(self.link, &mut bytes)); + + if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) { + if packet.header.proto == self.proto && + (packet.header.dst.equals(unsafe { IP_ADDR }) || packet.header.dst.equals(BROADCAST_IP_ADDR)) && + (packet.header.src.equals(self.peer_addr) || self.peer_addr.equals(BROADCAST_IP_ADDR)) { + for (b, d) in buf.iter_mut().zip(packet.data.iter()) { + *b = *d; + } + + return Ok(cmp::min(buf.len(), packet.data.len())); + } + } + + Ok(0) + } + + fn write(&mut self, buf: &[u8]) -> Result { + let ip_data = Vec::from(buf); + + self.id += 1; + let mut ip = Ipv4 { + header: Ipv4Header { + ver_hlen: 0x40 | (mem::size_of::() / 4 & 0xF) as u8, // No Options + services: 0, + len: n16::new((mem::size_of::() + ip_data.len()) as u16), // No Options + id: n16::new(self.id), + flags_fragment: n16::new(0), + ttl: 128, + proto: self.proto, + checksum: Checksum { data: 0 }, + src: unsafe { IP_ADDR }, + dst: self.peer_addr, + }, + options: Vec::new(), + data: ip_data, + }; + + unsafe { + let header_ptr: *const Ipv4Header = &ip.header; + ip.header.checksum.data = + Checksum::compile(Checksum::sum(header_ptr as usize, mem::size_of::()) + + Checksum::sum(ip.options.as_ptr() as usize, ip.options.len())); + } + + match syscall::write(self.link, &ip.to_bytes()) { + Ok(_) => Ok(buf.len()), + Err(err) => Err(err), + } + } + + fn sync(&mut self) -> Result { + syscall::fsync(self.link) + } +} + +impl Drop for IpResource { + fn drop(&mut self) { + let _ = syscall::close(self.link); + } +} diff --git a/schemes/ipd/src/scheme.rs b/schemes/ipd/src/scheme.rs new file mode 100644 index 0000000..6aa8034 --- /dev/null +++ b/schemes/ipd/src/scheme.rs @@ -0,0 +1,155 @@ +use std::cell::RefCell; +use std::rand; +use std::{str, u16}; + +use resource_scheme::ResourceScheme; +use syscall; +use syscall::error::{Error, Result, EACCES, ENOENT, EINVAL}; +use syscall::flag::O_RDWR; + +use common::{n16, MacAddr, Ipv4Addr, ArpHeader, Arp, Ipv4, MAC_ADDR, BROADCAST_MAC_ADDR, BROADCAST_IP_ADDR, IP_ADDR, IP_ROUTER_ADDR, IP_SUBNET}; +use resource::IpResource; + +/// A ARP entry (MAC + IP) +pub struct ArpEntry { + ip: Ipv4Addr, + mac: MacAddr, +} + +/// A IP scheme +pub struct IpScheme { + pub arp: RefCell>, +} + +impl IpScheme { + pub fn new() -> IpScheme { + IpScheme { + arp: RefCell::new(Vec::new()) + } + } +} + +impl ResourceScheme for IpScheme { + fn open_resource(&self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL)))); + let mut parts = path.split('/'); + if let Some(host_string) = parts.next() { + if let Some(proto_string) = parts.next() { + let proto = u8::from_str_radix(proto_string, 16).unwrap_or(0); + + if ! host_string.is_empty() { + let peer_addr = Ipv4Addr::from_str(host_string); + let mut route_mac = BROADCAST_MAC_ADDR; + + if ! peer_addr.equals(BROADCAST_IP_ADDR) { + let mut needs_routing = false; + + for octet in 0..4 { + let me = unsafe { IP_ADDR.bytes[octet] }; + let mask = unsafe { IP_SUBNET.bytes[octet] }; + let them = peer_addr.bytes[octet]; + if me & mask != them & mask { + needs_routing = true; + break; + } + } + + let route_addr = if needs_routing { + unsafe { IP_ROUTER_ADDR } + } else { + peer_addr + }; + + for entry in self.arp.borrow().iter() { + if entry.ip.equals(route_addr) { + route_mac = entry.mac; + break; + } + } + + if route_mac.equals(BROADCAST_MAC_ADDR) { + if let Ok(link) = syscall::open(&format!("ethernet:{}/806", &route_mac.to_string()), O_RDWR) { + let arp = Arp { + header: ArpHeader { + htype: n16::new(1), + ptype: n16::new(0x800), + hlen: 6, + plen: 4, + oper: n16::new(1), + src_mac: unsafe { MAC_ADDR }, + src_ip: unsafe { IP_ADDR }, + dst_mac: route_mac, + dst_ip: route_addr, + }, + data: Vec::new(), + }; + + match syscall::write(link, &arp.to_bytes()) { + Ok(_) => loop { + let mut bytes = [0; 65536]; + match syscall::read(link, &mut bytes) { + Ok(count) => if let Some(packet) = Arp::from_bytes(&bytes[..count]) { + if packet.header.oper.get() == 2 && + packet.header.src_ip.equals(route_addr) { + route_mac = packet.header.src_mac; + self.arp.borrow_mut().push(ArpEntry { + ip: route_addr, + mac: route_mac, + }); + break; + } + }, + Err(_) => (), + } + }, + Err(err) => println!("IP: ARP Write Failed: {}", err), + } + } + } + } + + if let Ok(link) = syscall::open(&format!("ethernet:{}/800", &route_mac.to_string()), O_RDWR) { + return Ok(Box::new(IpResource { + link: link, + data: Vec::new(), + peer_addr: peer_addr, + proto: proto, + id: (rand() % 65536) as u16, + })); + } + } else { + while let Ok(link) = syscall::open("ethernet:/800", O_RDWR) { + let mut bytes = [0; 65536]; + match syscall::read(link, &mut bytes) { + Ok(count) => { + if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) { + if packet.header.proto == proto && + (packet.header.dst.equals(unsafe { IP_ADDR }) || packet.header.dst.equals(BROADCAST_IP_ADDR)) { + return Ok(Box::new(IpResource { + link: link, + data: packet.data, + peer_addr: packet.header.src, + proto: proto, + id: (rand() % 65536) as u16, + })); + } + } + } + Err(_) => break, + } + } + } + } else { + println!("IP: No protocol provided"); + } + } else { + println!("IP: No host provided"); + } + + Err(Error::new(ENOENT)) + } else { + Err(Error::new(EACCES)) + } + } +} diff --git a/schemes/orbital b/schemes/orbital new file mode 160000 index 0000000..3667b89 --- /dev/null +++ b/schemes/orbital @@ -0,0 +1 @@ +Subproject commit 3667b8960255928363b88ac9ac5902f228823512 diff --git a/schemes/tcpd/Cargo.toml b/schemes/tcpd/Cargo.toml new file mode 100644 index 0000000..5b8c2c7 --- /dev/null +++ b/schemes/tcpd/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "tcpd" +version = "0.1.0" + +[dependencies] +resource_scheme = { path = "../../crates/resource_scheme/" } +syscall = { path = "../../syscall/" } diff --git a/schemes/tcpd/src/common.rs b/schemes/tcpd/src/common.rs new file mode 100644 index 0000000..76b5633 --- /dev/null +++ b/schemes/tcpd/src/common.rs @@ -0,0 +1,217 @@ +use std::{mem, slice, u8, u16, u32}; + +pub static mut IP_ADDR: Ipv4Addr = Ipv4Addr { bytes: [10, 0, 2, 15] }; + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +#[repr(packed)] +pub struct n16(u16); + +impl n16 { + pub fn new(value: u16) -> Self { + n16(value.to_be()) + } + + pub fn get(&self) -> u16 { + u16::from_be(self.0) + } + + pub fn set(&mut self, value: u16) { + self.0 = value.to_be(); + } +} + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +#[repr(packed)] +pub struct n32(u32); + +impl n32 { + pub fn new(value: u32) -> Self { + n32(value.to_be()) + } + + pub fn get(&self) -> u32 { + u32::from_be(self.0) + } + + pub fn set(&mut self, value: u32) { + self.0 = value.to_be(); + } +} + +#[derive(Copy, Clone)] +pub struct Ipv4Addr { + pub bytes: [u8; 4], +} + +impl Ipv4Addr { + pub fn equals(&self, other: Self) -> bool { + for i in 0..4 { + if self.bytes[i] != other.bytes[i] { + return false; + } + } + true + } + + pub fn from_str(string: &str) -> Self { + let mut addr = Ipv4Addr { bytes: [0, 0, 0, 0] }; + + let mut i = 0; + for part in string.split('.') { + let octet = part.parse::().unwrap_or(0); + match i { + 0 => addr.bytes[0] = octet, + 1 => addr.bytes[1] = octet, + 2 => addr.bytes[2] = octet, + 3 => addr.bytes[3] = octet, + _ => break, + } + i += 1; + } + + addr + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + + for i in 0..4 { + if i > 0 { + string = string + "."; + } + string = string + &format!("{}", self.bytes[i]); + } + + string + } +} + +#[derive(Copy, Clone)] +pub struct Checksum { + pub data: u16, +} + +impl Checksum { + pub unsafe fn check(&self, mut ptr: usize, mut len: usize) -> bool { + let mut sum: usize = 0; + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + sum == 0xFFFF + } + + pub unsafe fn calculate(&mut self, ptr: usize, len: usize) { + self.data = 0; + + let sum = Checksum::sum(ptr, len); + + self.data = Checksum::compile(sum); + } + + pub unsafe fn sum(mut ptr: usize, mut len: usize) -> usize { + let mut sum = 0; + + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + sum + } + + pub fn compile(mut sum: usize) -> u16 { + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + 0xFFFF - (sum as u16) + } +} + +pub const TCP_FIN: u16 = 1; +pub const TCP_SYN: u16 = 1 << 1; +pub const TCP_RST: u16 = 1 << 2; +pub const TCP_PSH: u16 = 1 << 3; +pub const TCP_ACK: u16 = 1 << 4; + +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct TcpHeader { + pub src: n16, + pub dst: n16, + pub sequence: n32, + pub ack_num: n32, + pub flags: n16, + pub window_size: n16, + pub checksum: Checksum, + pub urgent_pointer: n16, +} + +pub struct Tcp { + pub header: TcpHeader, + pub options: Vec, + pub data: Vec, +} + +impl Tcp { + pub fn checksum(&mut self, src_addr: &Ipv4Addr, dst_addr: &Ipv4Addr) { + self.header.checksum.data = 0; + + let proto = n16::new(0x06); + let segment_len = n16::new((mem::size_of::() + self.options.len() + self.data.len()) as u16); + self.header.checksum.data = Checksum::compile(unsafe { + Checksum::sum(src_addr.bytes.as_ptr() as usize, src_addr.bytes.len()) + + Checksum::sum(dst_addr.bytes.as_ptr() as usize, dst_addr.bytes.len()) + + Checksum::sum((&proto as *const n16) as usize, mem::size_of::()) + + Checksum::sum((&segment_len as *const n16) as usize, mem::size_of::()) + + Checksum::sum((&self.header as *const TcpHeader) as usize, mem::size_of::()) + + Checksum::sum(self.options.as_ptr() as usize, self.options.len()) + + Checksum::sum(self.data.as_ptr() as usize, self.data.len()) + }); + } + + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() >= mem::size_of::() { + unsafe { + let header = *(bytes.as_ptr() as *const TcpHeader); + let header_len = ((header.flags.get() & 0xF000) >> 10) as usize; + + return Some(Tcp { + header: header, + options: bytes[mem::size_of::()..header_len].to_vec(), + data: bytes[header_len..bytes.len()].to_vec(), + }); + } + } + None + } + + pub fn to_bytes(&self) -> Vec { + unsafe { + let header_ptr: *const TcpHeader = &self.header; + let mut ret = Vec::from(slice::from_raw_parts(header_ptr as *const u8, + mem::size_of::())); + ret.extend_from_slice(&self.options); + ret.extend_from_slice(&self.data); + ret + } + } +} diff --git a/schemes/tcpd/src/main.rs b/schemes/tcpd/src/main.rs new file mode 100644 index 0000000..cea3920 --- /dev/null +++ b/schemes/tcpd/src/main.rs @@ -0,0 +1,30 @@ +#![feature(rand)] + +extern crate resource_scheme; +extern crate syscall; + +use std::fs::File; +use std::io::{Read, Write}; +use std::thread; + +use resource_scheme::ResourceScheme; +use syscall::Packet; + +use scheme::TcpScheme; + +pub mod common; +pub mod resource; +pub mod scheme; + +fn main() { + thread::spawn(move || { + let mut socket = File::create(":tcp").expect("tcpd: failed to create tcp scheme"); + let scheme = TcpScheme; + loop { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("tcpd: failed to read events from tcp scheme"); + scheme.handle(&mut packet); + socket.write(&packet).expect("tcpd: failed to write responses to tcp scheme"); + } + }); +} diff --git a/schemes/tcpd/src/resource.rs b/schemes/tcpd/src/resource.rs new file mode 100644 index 0000000..ea71882 --- /dev/null +++ b/schemes/tcpd/src/resource.rs @@ -0,0 +1,328 @@ +use std::{cmp, mem}; +use std::cell::UnsafeCell; +use std::sync::Arc; + +use resource_scheme::Resource; +use syscall; +use syscall::error::*; + +use common::{n16, n32, Ipv4Addr, Checksum, IP_ADDR, Tcp, TcpHeader, TCP_SYN, TCP_PSH, TCP_FIN, TCP_ACK}; + +pub struct TcpStream { + pub ip: usize, + pub peer_addr: Ipv4Addr, + pub peer_port: u16, + pub host_port: u16, + pub sequence: u32, + pub acknowledge: u32, + pub finished: bool +} + +impl TcpStream { + fn path(&self, buf: &mut [u8]) -> Result { + let path_string = format!("tcp:{}:{}/{}", self.peer_addr.to_string(), self.peer_port, self.host_port); + let path = path_string.as_bytes(); + + for (b, p) in buf.iter_mut().zip(path.iter()) { + *b = *p; + } + + Ok(cmp::min(buf.len(), path.len())) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + if self.finished { + return Ok(0); + } + + loop { + let mut bytes = [0; 65536]; + let count = try!(syscall::read(self.ip, &mut bytes)); + + if let Some(segment) = Tcp::from_bytes(&bytes[..count]) { + if segment.header.dst.get() == self.host_port && segment.header.src.get() == self.peer_port { + //println!("Read: {}=={} {:X}: {}", segment.header.sequence.get(), self.acknowledge, segment.header.flags.get(), segment.data.len()); + + if self.acknowledge == segment.header.sequence.get() { + if segment.header.flags.get() & TCP_FIN == TCP_FIN { + self.finished = true; + } + + if segment.header.flags.get() & (TCP_SYN | TCP_ACK) == TCP_ACK { + let flags = if self.finished { + TCP_ACK | TCP_FIN + } else { + TCP_ACK + }; + + // Send ACK + self.acknowledge += segment.data.len() as u32; + let mut tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new(((mem::size_of::() << 10) & 0xF000) as u16 | flags), + window_size: n16::new(65535), + checksum: Checksum { + data: 0 + }, + urgent_pointer: n16::new(0) + }, + options: Vec::new(), + data: Vec::new() + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + //println!("Sending read ack: {} {} {:X}", tcp.header.sequence.get(), tcp.header.ack_num.get(), tcp.header.flags.get()); + + let _ = syscall::write(self.ip, &tcp.to_bytes()); + + // TODO: Support broken packets (one packet in two buffers) + let mut i = 0; + while i < buf.len() && i < segment.data.len() { + buf[i] = segment.data[i]; + i += 1; + } + return Ok(i); + } + } else { + println!("TCP: MISMATCH: {}=={}", segment.header.sequence.get(), self.acknowledge); + } + } else { + println!("TCP: WRONG PORT {}=={} && {}=={}", segment.header.dst.get(), self.host_port, segment.header.src.get(), self.peer_port); + } + } + } + } + + fn write(&mut self, buf: &[u8]) -> Result { + let tcp_data = Vec::from(buf); + + let mut tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new((((mem::size_of::()) << 10) & 0xF000) as u16 | TCP_PSH | + TCP_ACK), + window_size: n16::new(65535), + checksum: Checksum { data: 0 }, + urgent_pointer: n16::new(0), + }, + options: Vec::new(), + data: tcp_data, + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + match syscall::write(self.ip, &tcp.to_bytes()) { + Ok(size) => { + loop { + // Wait for ACK + let mut bytes = [0; 65536]; + match syscall::read(self.ip, &mut bytes) { + Ok(count) => { + if let Some(segment) = Tcp::from_bytes(&bytes[..count]) { + if segment.header.dst.get() == self.host_port && + segment.header.src.get() == self.peer_port { + return if (segment.header.flags.get() & (TCP_SYN | TCP_ACK)) == TCP_ACK { + self.sequence = segment.header.ack_num.get(); + self.acknowledge = segment.header.sequence.get(); + Ok(size) + } else { + Err(Error::new(EPIPE)) + }; + } + } + } + Err(err) => return Err(err), + } + } + } + Err(err) => Err(err), + } + } + + fn sync(&mut self) -> Result { + syscall::fsync(self.ip) + } + + /// Etablish client + pub fn client_establish(&mut self) -> bool { + // Send SYN + let mut tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new(((mem::size_of::() << 10) & 0xF000) as u16 | TCP_SYN), + window_size: n16::new(65535), + checksum: Checksum { data: 0 }, + urgent_pointer: n16::new(0), + }, + options: Vec::new(), + data: Vec::new(), + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + match syscall::write(self.ip, &tcp.to_bytes()) { + Ok(_) => { + loop { + // Wait for SYN-ACK + let mut bytes = [0; 65536]; + match syscall::read(self.ip, &mut bytes) { + Ok(count) => { + if let Some(segment) = Tcp::from_bytes(&bytes[..count]) { + if segment.header.dst.get() == self.host_port && + segment.header.src.get() == self.peer_port { + return if segment.header.flags.get() & (TCP_SYN | TCP_ACK) == TCP_SYN | TCP_ACK { + self.sequence = segment.header.ack_num.get(); + self.acknowledge = segment.header.sequence.get(); + + self.acknowledge += 1; + tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new(((mem::size_of::() << 10) & 0xF000) as u16 | TCP_ACK), + window_size: n16::new(65535), + checksum: Checksum { + data: 0 + }, + urgent_pointer: n16::new(0) + }, + options: Vec::new(), + data: Vec::new() + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + let _ = syscall::write(self.ip, &tcp.to_bytes()); + + true + } else { + false + }; + } + } + } + Err(_) => return false, + } + } + } + Err(_) => false, + } + } + + /// Try to establish a server connection + pub fn server_establish(&mut self, _: Tcp) -> bool { + // Send SYN-ACK + self.acknowledge += 1; + let mut tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new(((mem::size_of::() << 10) & 0xF000) as u16 | TCP_SYN | + TCP_ACK), + window_size: n16::new(65535), + checksum: Checksum { data: 0 }, + urgent_pointer: n16::new(0), + }, + options: Vec::new(), + data: Vec::new(), + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + match syscall::write(self.ip, &tcp.to_bytes()) { + Ok(_) => { + loop { + // Wait for ACK + let mut bytes = [0; 65536]; + match syscall::read(self.ip, &mut bytes) { + Ok(count ) => { + if let Some(segment) = Tcp::from_bytes(&bytes[..count]) { + if segment.header.dst.get() == self.host_port && + segment.header.src.get() == self.peer_port { + return if segment.header.flags.get() & (TCP_SYN | TCP_ACK) == TCP_ACK { + self.sequence = segment.header.ack_num.get(); + self.acknowledge = segment.header.sequence.get(); + true + } else { + false + }; + } + } + } + Err(_) => return false, + } + } + } + Err(_) => false, + } + } +} + +impl Drop for TcpStream { + fn drop(&mut self) { + // Send FIN-ACK + let mut tcp = Tcp { + header: TcpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + sequence: n32::new(self.sequence), + ack_num: n32::new(self.acknowledge), + flags: n16::new((((mem::size_of::()) << 10) & 0xF000) as u16 | TCP_FIN | TCP_ACK), + window_size: n16::new(65535), + checksum: Checksum { data: 0 }, + urgent_pointer: n16::new(0), + }, + options: Vec::new(), + data: Vec::new(), + }; + + tcp.checksum(& unsafe { IP_ADDR }, &self.peer_addr); + + let _ = syscall::write(self.ip, &tcp.to_bytes()); + let _ = syscall::close(self.ip); + } +} + +/// A TCP resource +pub struct TcpResource { + pub stream: Arc> +} + +impl Resource for TcpResource { + fn dup(&self) -> Result> { + Ok(Box::new(TcpResource { + stream: self.stream.clone() + })) + } + + fn path(&self, buf: &mut [u8]) -> Result { + unsafe { (*self.stream.get()).path(buf) } + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + unsafe { (*self.stream.get()).read(buf) } + } + + fn write(&mut self, buf: &[u8]) -> Result { + unsafe { (*self.stream.get()).write(buf) } + } + + fn sync(&mut self) -> Result { + unsafe { (*self.stream.get()).sync() } + } +} diff --git a/schemes/tcpd/src/scheme.rs b/schemes/tcpd/src/scheme.rs new file mode 100644 index 0000000..302b048 --- /dev/null +++ b/schemes/tcpd/src/scheme.rs @@ -0,0 +1,94 @@ +use std::cell::UnsafeCell; +use std::rand; +use std::sync::Arc; +use std::{str, u16}; + +use resource_scheme::ResourceScheme; +use syscall; +use syscall::error::{Error, Result, ENOENT, EINVAL}; +use syscall::flag::O_RDWR; + +use common::{Ipv4Addr, Tcp, TCP_SYN, TCP_ACK}; +use resource::{TcpResource, TcpStream}; + +/// A TCP scheme +pub struct TcpScheme; + +impl ResourceScheme for TcpScheme { + fn open_resource(&self, url: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result> { + let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL)))); + let mut parts = path.split('/'); + let remote = parts.next().unwrap_or(""); + let path = parts.next().unwrap_or(""); + + let mut remote_parts = remote.split(':'); + let host = remote_parts.next().unwrap_or(""); + let port = remote_parts.next().unwrap_or(""); + + if ! host.is_empty() && ! port.is_empty() { + let peer_addr = Ipv4Addr::from_str(host); + let peer_port = port.parse::().unwrap_or(0); + let host_port = (rand() % 32768 + 32768) as u16; + + match syscall::open(&format!("ip:{}/6", peer_addr.to_string()), O_RDWR) { + Ok(ip) => { + let mut stream = TcpStream { + ip: ip, + peer_addr: peer_addr, + peer_port: peer_port, + host_port: host_port, + sequence: rand() as u32, + acknowledge: 0, + finished: false + }; + + if stream.client_establish() { + return Ok(Box::new(TcpResource { + stream: Arc::new(UnsafeCell::new(stream)) + })); + } + } + Err(err) => return Err(err), + } + } else if ! path.is_empty() { + let host_port = path.parse::().unwrap_or(0); + + while let Ok(ip) = syscall::open("ip:/6", O_RDWR) { + let mut bytes = [0; 65536]; + match syscall::read(ip, &mut bytes) { + Ok(count) => { + if let Some(segment) = Tcp::from_bytes(&bytes[..count]) { + if segment.header.dst.get() == host_port && segment.header.flags.get() & (TCP_SYN | TCP_ACK) == TCP_SYN { + let mut path = [0; 256]; + if let Ok(path_count) = syscall::fpath(ip, &mut path) { + let ip_reference = unsafe { str::from_utf8_unchecked(&path[.. path_count]) }.split(':').nth(1).unwrap_or(""); + let ip_remote = ip_reference.split('/').next().unwrap_or(""); + let peer_addr = ip_remote.split(':').next().unwrap_or(""); + + let mut stream = TcpStream { + ip: ip, + peer_addr: Ipv4Addr::from_str(peer_addr), + peer_port: segment.header.src.get(), + host_port: host_port, + sequence: rand() as u32, + acknowledge: segment.header.sequence.get(), + finished: false + }; + + if stream.server_establish(segment) { + return Ok(Box::new(TcpResource { + stream: Arc::new(UnsafeCell::new(stream)) + })); + } + } + } + } + } + Err(err) => return Err(err), + } + } + } + + Err(Error::new(ENOENT)) + } +} diff --git a/schemes/udpd/Cargo.toml b/schemes/udpd/Cargo.toml new file mode 100644 index 0000000..9a2ae6a --- /dev/null +++ b/schemes/udpd/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "udpd" +version = "0.1.0" + +[dependencies] +resource_scheme = { path = "../../crates/resource_scheme/" } +syscall = { path = "../../syscall/" } diff --git a/schemes/udpd/src/common.rs b/schemes/udpd/src/common.rs new file mode 100644 index 0000000..3ffdb4c --- /dev/null +++ b/schemes/udpd/src/common.rs @@ -0,0 +1,167 @@ +use std::{mem, slice, u8, u16}; + +pub static mut IP_ADDR: Ipv4Addr = Ipv4Addr { bytes: [10, 0, 2, 15] }; + +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +#[repr(packed)] +pub struct n16(u16); + +impl n16 { + pub fn new(value: u16) -> Self { + n16(value.to_be()) + } + + pub fn get(&self) -> u16 { + u16::from_be(self.0) + } + + pub fn set(&mut self, value: u16) { + self.0 = value.to_be(); + } +} + +#[derive(Copy, Clone)] +pub struct Ipv4Addr { + pub bytes: [u8; 4], +} + +impl Ipv4Addr { + pub fn equals(&self, other: Self) -> bool { + for i in 0..4 { + if self.bytes[i] != other.bytes[i] { + return false; + } + } + true + } + + pub fn from_str(string: &str) -> Self { + let mut addr = Ipv4Addr { bytes: [0, 0, 0, 0] }; + + let mut i = 0; + for part in string.split('.') { + let octet = part.parse::().unwrap_or(0); + match i { + 0 => addr.bytes[0] = octet, + 1 => addr.bytes[1] = octet, + 2 => addr.bytes[2] = octet, + 3 => addr.bytes[3] = octet, + _ => break, + } + i += 1; + } + + addr + } + + pub fn to_string(&self) -> String { + let mut string = String::new(); + + for i in 0..4 { + if i > 0 { + string = string + "."; + } + string = string + &format!("{}", self.bytes[i]); + } + + string + } +} + +#[derive(Copy, Clone)] +pub struct Checksum { + pub data: u16, +} + +impl Checksum { + pub unsafe fn check(&self, mut ptr: usize, mut len: usize) -> bool { + let mut sum: usize = 0; + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + sum == 0xFFFF + } + + pub unsafe fn calculate(&mut self, ptr: usize, len: usize) { + self.data = 0; + + let sum = Checksum::sum(ptr, len); + + self.data = Checksum::compile(sum); + } + + pub unsafe fn sum(mut ptr: usize, mut len: usize) -> usize { + let mut sum = 0; + + while len > 1 { + sum += *(ptr as *const u16) as usize; + len -= 2; + ptr += 2; + } + + if len > 0 { + sum += *(ptr as *const u8) as usize; + } + + sum + } + + pub fn compile(mut sum: usize) -> u16 { + while (sum >> 16) > 0 { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + 0xFFFF - (sum as u16) + } +} + +#[derive(Copy, Clone)] +#[repr(packed)] +pub struct UdpHeader { + pub src: n16, + pub dst: n16, + pub len: n16, + pub checksum: Checksum, +} + +pub struct Udp { + pub header: UdpHeader, + pub data: Vec, +} + +impl Udp { + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() >= mem::size_of::() { + unsafe { + Option::Some(Udp { + header: *(bytes.as_ptr() as *const UdpHeader), + data: bytes[mem::size_of::()..bytes.len()].to_vec(), + }) + } + } else { + Option::None + } + } + + pub fn to_bytes(&self) -> Vec { + unsafe { + let header_ptr: *const UdpHeader = &self.header; + let mut ret = Vec::from(slice::from_raw_parts(header_ptr as *const u8, + mem::size_of::())); + ret.extend_from_slice(&self.data); + ret + } + } +} diff --git a/schemes/udpd/src/main.rs b/schemes/udpd/src/main.rs new file mode 100644 index 0000000..e6eccaa --- /dev/null +++ b/schemes/udpd/src/main.rs @@ -0,0 +1,30 @@ +#![feature(rand)] + +extern crate resource_scheme; +extern crate syscall; + +use std::fs::File; +use std::io::{Read, Write}; +use std::thread; + +use resource_scheme::ResourceScheme; +use syscall::Packet; + +use scheme::UdpScheme; + +pub mod common; +pub mod resource; +pub mod scheme; + +fn main() { + thread::spawn(move || { + let mut socket = File::create(":udp").expect("udpd: failed to create udp scheme"); + let scheme = UdpScheme; + loop { + let mut packet = Packet::default(); + socket.read(&mut packet).expect("udpd: failed to read events from udp scheme"); + scheme.handle(&mut packet); + socket.write(&packet).expect("udpd: failed to write responses to udp scheme"); + } + }); +} diff --git a/schemes/udpd/src/resource.rs b/schemes/udpd/src/resource.rs new file mode 100644 index 0000000..a11dcd5 --- /dev/null +++ b/schemes/udpd/src/resource.rs @@ -0,0 +1,114 @@ +use std::{cmp, mem}; + +use resource_scheme::Resource; +use syscall; +use syscall::error::*; + +use common::{n16, Ipv4Addr, Checksum, IP_ADDR, Udp, UdpHeader}; + +/// UDP resource +pub struct UdpResource { + pub ip: usize, + pub data: Vec, + pub peer_addr: Ipv4Addr, + pub peer_port: u16, + pub host_port: u16, +} + +impl Resource for UdpResource { + fn dup(&self) -> Result> { + match syscall::dup(self.ip) { + Ok(ip) => { + Ok(Box::new(UdpResource { + ip: ip, + data: self.data.clone(), + peer_addr: self.peer_addr, + peer_port: self.peer_port, + host_port: self.host_port, + })) + } + Err(err) => Err(err), + } + } + + fn path(&self, buf: &mut [u8]) -> Result { + let path_string = format!("udp:{}:{}/{}", self.peer_addr.to_string(), self.peer_port, self.host_port); + let path = path_string.as_bytes(); + + for (b, p) in buf.iter_mut().zip(path.iter()) { + *b = *p; + } + + Ok(cmp::min(buf.len(), path.len())) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + if ! self.data.is_empty() { + let mut bytes: Vec = Vec::new(); + mem::swap(&mut self.data, &mut bytes); + + // TODO: Allow splitting + let mut i = 0; + while i < buf.len() && i < bytes.len() { + buf[i] = bytes[i]; + i += 1; + } + + return Ok(i); + } + + loop { + let mut bytes = [0; 65536]; + let count = try!(syscall::read(self.ip, &mut bytes)); + + if let Some(datagram) = Udp::from_bytes(&bytes[..count]) { + if datagram.header.dst.get() == self.host_port && + datagram.header.src.get() == self.peer_port { + // TODO: Allow splitting + let mut i = 0; + while i < buf.len() && i < datagram.data.len() { + buf[i] = datagram.data[i]; + i += 1; + } + + return Ok(i); + } + } + } + } + + fn write(&mut self, buf: &[u8]) -> Result { + let mut udp = Udp { + header: UdpHeader { + src: n16::new(self.host_port), + dst: n16::new(self.peer_port), + len: n16::new((mem::size_of::() + buf.len()) as u16), + checksum: Checksum { data: 0 }, + }, + data: Vec::from(buf), + }; + + unsafe { + let proto = n16::new(0x11); + let datagram_len = n16::new((mem::size_of::() + udp.data.len()) as u16); + udp.header.checksum.data = + Checksum::compile(Checksum::sum((&IP_ADDR as *const Ipv4Addr) as usize, + mem::size_of::()) + + Checksum::sum((&self.peer_addr as *const Ipv4Addr) as usize, + mem::size_of::()) + + Checksum::sum((&proto as *const n16) as usize, + mem::size_of::()) + + Checksum::sum((&datagram_len as *const n16) as usize, + mem::size_of::()) + + Checksum::sum((&udp.header as *const UdpHeader) as usize, + mem::size_of::()) + + Checksum::sum(udp.data.as_ptr() as usize, udp.data.len())); + } + + syscall::write(self.ip, &udp.to_bytes()).and(Ok(buf.len())) + } + + fn sync(&mut self) -> Result { + syscall::fsync(self.ip) + } +} diff --git a/schemes/udpd/src/scheme.rs b/schemes/udpd/src/scheme.rs new file mode 100644 index 0000000..91d9f62 --- /dev/null +++ b/schemes/udpd/src/scheme.rs @@ -0,0 +1,69 @@ +use std::rand; +use std::{str, u16}; + +use resource_scheme::ResourceScheme; +use syscall; +use syscall::error::{Error, Result, ENOENT, EINVAL}; +use syscall::flag::O_RDWR; + +use common::{Ipv4Addr, Udp}; +use resource::UdpResource; + +/// UDP UdpScheme +pub struct UdpScheme; + +impl ResourceScheme for UdpScheme { + fn open_resource(&self, url: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result> { + let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL)))); + let mut parts = path.split('/'); + let remote = parts.next().unwrap_or(""); + let path = parts.next().unwrap_or(""); + + // Check host and port vs path + if remote.is_empty() { + let host_port = path.parse::().unwrap_or(0); + if host_port > 0 { + while let Ok(ip) = syscall::open("ip:/11", O_RDWR) { + let mut bytes = [0; 65536]; + if let Ok(count) = syscall::read(ip, &mut bytes) { + if let Some(datagram) = Udp::from_bytes(&bytes[..count]) { + if datagram.header.dst.get() == host_port { + let mut path = [0; 256]; + if let Ok(path_count) = syscall::fpath(ip, &mut path) { + let ip_reference = unsafe { str::from_utf8_unchecked(&path[.. path_count]) }.split(':').nth(1).unwrap_or(""); + let peer_addr = ip_reference.split('/').next().unwrap_or("").split(':').next().unwrap_or(""); + + return Ok(Box::new(UdpResource { + ip: ip, + data: datagram.data, + peer_addr: Ipv4Addr::from_str(peer_addr), + peer_port: datagram.header.src.get(), + host_port: host_port, + })); + } + } + } + } + } + } + } else { + let mut remote_parts = remote.split(':'); + let peer_addr = remote_parts.next().unwrap_or(""); + let peer_port = remote_parts.next().unwrap_or("").parse::().unwrap_or(0); + if peer_port > 0 { + let host_port = path.parse::().unwrap_or((rand() % 32768 + 32768) as u16); + if let Ok(ip) = syscall::open(&format!("ip:{}/11", peer_addr), O_RDWR) { + return Ok(Box::new(UdpResource { + ip: ip, + data: Vec::new(), + peer_addr: Ipv4Addr::from_str(peer_addr), + peer_port: peer_port as u16, + host_port: host_port, + })); + } + } + } + + Err(Error::new(ENOENT)) + } +} diff --git a/syscall/src/scheme.rs b/syscall/src/scheme.rs index 310bb88..a84865f 100644 --- a/syscall/src/scheme.rs +++ b/syscall/src/scheme.rs @@ -98,3 +98,99 @@ pub trait Scheme { Err(Error::new(EBADF)) } } + +pub trait SchemeMut { + fn handle(&mut self, packet: &mut Packet) { + packet.a = Error::mux(match packet.a { + SYS_OPEN => self.open(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d, packet.uid, packet.gid), + SYS_MKDIR => self.mkdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.d as u16, packet.uid, packet.gid), + SYS_RMDIR => self.rmdir(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.uid, packet.gid), + SYS_UNLINK => self.unlink(unsafe { slice::from_raw_parts(packet.b as *const u8, packet.c) }, packet.uid, packet.gid), + + SYS_DUP => self.dup(packet.b), + SYS_READ => self.read(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }), + SYS_WRITE => self.write(packet.b, unsafe { slice::from_raw_parts(packet.c as *const u8, packet.d) }), + SYS_LSEEK => self.seek(packet.b, packet.c, packet.d), + SYS_FEVENT => self.fevent(packet.b, packet.c), + SYS_FPATH => self.fpath(packet.b, unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) }), + SYS_FSTAT => self.fstat(packet.b, unsafe { &mut *(packet.c as *mut Stat) }), + SYS_FSYNC => self.fsync(packet.b), + SYS_FTRUNCATE => self.ftruncate(packet.b, packet.c), + SYS_CLOSE => self.close(packet.b), + + _ => Err(Error::new(ENOSYS)) + }); + } + + /* Scheme operations */ + #[allow(unused_variables)] + fn open(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + #[allow(unused_variables)] + fn mkdir(&mut self, path: &[u8], mode: u16, uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + #[allow(unused_variables)] + fn rmdir(&mut self, path: &[u8], uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + #[allow(unused_variables)] + fn unlink(&mut self, path: &[u8], uid: u32, gid: u32) -> Result { + Err(Error::new(ENOENT)) + } + + /* Resource operations */ + #[allow(unused_variables)] + fn dup(&mut self, old_id: usize) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn write(&mut self, id: usize, buf: &[u8]) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn fevent(&mut self, id: usize, flags: usize) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn fsync(&mut self, id: usize) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn ftruncate(&mut self, id: usize, len: usize) -> Result { + Err(Error::new(EBADF)) + } + + #[allow(unused_variables)] + fn close(&mut self, id: usize) -> Result { + Err(Error::new(EBADF)) + } +}