From ff06f2d19f0363702ef7f282bcf5cef0ca2a82ce Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 4 Jan 2017 16:34:43 -0700 Subject: [PATCH 1/6] Use cargo workspace (which will potentially improve build times --- Cargo.toml | 34 +++++++++++++++++ Makefile | 108 ++++------------------------------------------------- kernel | 2 +- 3 files changed, 43 insertions(+), 101 deletions(-) create mode 100644 Cargo.toml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d6329de --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,34 @@ +[workspace] +members = [ + "drivers/ahcid", + "drivers/bgad", + "drivers/e1000d", + "drivers/pcid", + "drivers/ps2d", + "drivers/rtl8168d", + "drivers/vesad", + "kernel", + "programs/acid", + "programs/binutils", + "programs/contain", + "programs/coreutils", + "programs/extrautils", + "programs/games", + "programs/init", + "programs/ion", + "programs/netutils", + "programs/orbutils", + "programs/pkgutils", + "programs/smith", + "programs/tar", + "programs/userutils", + "schemes/ethernetd", + "schemes/example", + "schemes/ipd", + "schemes/orbital", + "schemes/ptyd", + "schemes/randd", + "schemes/redoxfs", + "schemes/tcpd", + "schemes/udpd" +] diff --git a/Makefile b/Makefile index 4cbd23c..9592719 100644 --- a/Makefile +++ b/Makefile @@ -37,38 +37,9 @@ iso: build/livedisk.iso FORCE: clean: + cargo clean cargo clean --manifest-path rust/src/libstd/Cargo.toml - cargo clean --manifest-path kernel/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/rtl8168d/Cargo.toml - cargo clean --manifest-path drivers/vesad/Cargo.toml - cargo clean --manifest-path programs/acid/Cargo.toml - cargo clean --manifest-path programs/contain/Cargo.toml - cargo clean --manifest-path programs/init/Cargo.toml - cargo clean --manifest-path programs/ion/Cargo.toml - cargo clean --manifest-path programs/binutils/Cargo.toml - cargo clean --manifest-path programs/coreutils/Cargo.toml - cargo clean --manifest-path programs/extrautils/Cargo.toml - cargo clean --manifest-path programs/games/Cargo.toml - cargo clean --manifest-path programs/netutils/Cargo.toml - cargo clean --manifest-path programs/orbutils/Cargo.toml - cargo clean --manifest-path programs/pkgutils/Cargo.toml - cargo clean --manifest-path programs/userutils/Cargo.toml - cargo clean --manifest-path programs/smith/Cargo.toml - cargo clean --manifest-path programs/tar/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/ptyd/Cargo.toml - cargo clean --manifest-path schemes/randd/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 - -$(FUMOUNT) build/filesystem/ + -$(FUMOUNT) build/filesystem/ || true rm -rf initfs/bin rm -rf filesystem/bin filesystem/sbin filesystem/ui/bin rm -rf build @@ -92,71 +63,8 @@ ref: FORCE cargo run --manifest-path crates/docgen/Cargo.toml -- programs/extrautils/src/bin/ filesystem/ref/ cargo run --manifest-path crates/docgen/Cargo.toml -- programs/netutils/src/ filesystem/ref/ -test: - cargo test --manifest-path rust/src/libstd/Cargo.toml - cargo test --manifest-path kernel/Cargo.toml - cargo test --manifest-path drivers/ahcid/Cargo.toml - cargo test --manifest-path drivers/e1000d/Cargo.toml - cargo test --manifest-path drivers/ps2d/Cargo.toml - cargo test --manifest-path drivers/pcid/Cargo.toml - cargo test --manifest-path drivers/rtl8168d/Cargo.toml - cargo test --manifest-path drivers/vesad/Cargo.toml - cargo test --manifest-path programs/acid/Cargo.toml - cargo test --manifest-path programs/contain/Cargo.toml - cargo test --manifest-path programs/init/Cargo.toml - cargo test --manifest-path programs/ion/Cargo.toml - cargo test --manifest-path programs/binutils/Cargo.toml - cargo test --manifest-path programs/coreutils/Cargo.toml - cargo test --manifest-path programs/extrautils/Cargo.toml - cargo test --manifest-path programs/games/Cargo.toml - cargo test --manifest-path programs/netutils/Cargo.toml - cargo test --manifest-path programs/orbutils/Cargo.toml - cargo test --manifest-path programs/pkgutils/Cargo.toml - cargo test --manifest-path programs/userutils/Cargo.toml - cargo test --manifest-path programs/smith/Cargo.toml - cargo test --manifest-path programs/tar/Cargo.toml - cargo test --manifest-path schemes/ethernetd/Cargo.toml - cargo test --manifest-path schemes/example/Cargo.toml - cargo test --manifest-path schemes/ipd/Cargo.toml - cargo test --manifest-path schemes/orbital/Cargo.toml - cargo test --manifest-path schemes/ptyd/Cargo.toml - cargo test --manifest-path schemes/randd/Cargo.toml - cargo test --manifest-path schemes/redoxfs/Cargo.toml - cargo test --manifest-path schemes/tcpd/Cargo.toml - cargo test --manifest-path schemes/udpd/Cargo.toml - update: - #cargo update --manifest-path rust/src/libstd/Cargo.toml - cargo update --manifest-path kernel/Cargo.toml - cargo update --manifest-path drivers/ahcid/Cargo.toml - cargo update --manifest-path drivers/e1000d/Cargo.toml - cargo update --manifest-path drivers/ps2d/Cargo.toml - cargo update --manifest-path drivers/pcid/Cargo.toml - cargo update --manifest-path drivers/rtl8168d/Cargo.toml - cargo update --manifest-path drivers/vesad/Cargo.toml - cargo update --manifest-path programs/acid/Cargo.toml - cargo update --manifest-path programs/contain/Cargo.toml - cargo update --manifest-path programs/init/Cargo.toml - cargo update --manifest-path programs/ion/Cargo.toml - cargo update --manifest-path programs/binutils/Cargo.toml - cargo update --manifest-path programs/coreutils/Cargo.toml - cargo update --manifest-path programs/extrautils/Cargo.toml - cargo update --manifest-path programs/games/Cargo.toml - cargo update --manifest-path programs/netutils/Cargo.toml - cargo update --manifest-path programs/orbutils/Cargo.toml - cargo update --manifest-path programs/pkgutils/Cargo.toml - cargo update --manifest-path programs/userutils/Cargo.toml - cargo update --manifest-path programs/smith/Cargo.toml - cargo update --manifest-path programs/tar/Cargo.toml - cargo update --manifest-path schemes/ethernetd/Cargo.toml - cargo update --manifest-path schemes/example/Cargo.toml - cargo update --manifest-path schemes/ipd/Cargo.toml - cargo update --manifest-path schemes/orbital/Cargo.toml - cargo update --manifest-path schemes/ptyd/Cargo.toml - cargo update --manifest-path schemes/randd/Cargo.toml - cargo update --manifest-path schemes/redoxfs/Cargo.toml - cargo update --manifest-path schemes/tcpd/Cargo.toml - cargo update --manifest-path schemes/udpd/Cargo.toml + cargo update pull: git pull --rebase --recurse-submodules @@ -591,13 +499,13 @@ build/filesystem.bin: \ filesystem/bin/sh \ filesystem/bin/smith \ filesystem/bin/tar - -$(FUMOUNT) build/filesystem/ + -$(FUMOUNT) build/filesystem/ || true rm -rf $@ build/filesystem/ dd if=/dev/zero of=$@ bs=1048576 count=64 cargo run --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs-mkfs $@ mkdir -p build/filesystem/ cargo build --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs - schemes/redoxfs/target/release/redoxfs $@ build/filesystem/ + cargo run --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs -- $@ build/filesystem/ sleep 2 pgrep redoxfs cp -RL filesystem/* build/filesystem/ @@ -617,19 +525,19 @@ build/filesystem.bin: \ mkdir build/filesystem/tmp chmod 1777 build/filesystem/tmp sync - -$(FUMOUNT) build/filesystem/ + -$(FUMOUNT) build/filesystem/ || true rm -rf build/filesystem/ mount: FORCE mkdir -p build/filesystem/ cargo build --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs - schemes/redoxfs/target/release/redoxfs build/harddrive.bin build/filesystem/ + cargo run --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs -- build/harddrive.bin build/filesystem/ sleep 2 pgrep redoxfs unmount: FORCE sync - -$(FUMOUNT) build/filesystem/ + -$(FUMOUNT) build/filesystem/ || true rm -rf build/filesystem/ wireshark: FORCE diff --git a/kernel b/kernel index 882e64b..06118a2 160000 --- a/kernel +++ b/kernel @@ -1 +1 @@ -Subproject commit 882e64bdb976b365f055d3b0e225ced06dec8b49 +Subproject commit 06118a23ddf578424c35c2a0f9b704acbc5648f0 From 477c7379ead024f779cd5d21bdac2d960652bd52 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 4 Jan 2017 16:46:18 -0700 Subject: [PATCH 2/6] Cleanup dependencies --- drivers/ps2d/Cargo.toml | 2 +- drivers/vesad/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/ps2d/Cargo.toml b/drivers/ps2d/Cargo.toml index 98ebbf2..22863b9 100644 --- a/drivers/ps2d/Cargo.toml +++ b/drivers/ps2d/Cargo.toml @@ -6,5 +6,5 @@ version = "0.1.0" bitflags = "*" event = { path = "../../crates/event/" } io = { path = "../../crates/io/" } -orbclient = "0.1" +orbclient = "0.2" redox_syscall = { path = "../../syscall/" } diff --git a/drivers/vesad/Cargo.toml b/drivers/vesad/Cargo.toml index 9041edb..a70dbf0 100644 --- a/drivers/vesad/Cargo.toml +++ b/drivers/vesad/Cargo.toml @@ -3,9 +3,9 @@ name = "vesad" version = "0.1.0" [dependencies] -orbclient = "0.1" +orbclient = "0.2" ransid = "0.2" -rusttype = { git = "https://github.com/dylanede/rusttype.git", optional = true } +rusttype = { version = "0.2", optional = true } redox_syscall = { path = "../../syscall" } [features] From 2eda65945f05daacff449927659c6fdac5d412e6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 4 Jan 2017 17:11:22 -0700 Subject: [PATCH 3/6] Simplify travis build --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index efb4c03..d8e9d69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,10 @@ rust: cache: cargo os: - linux -- osx -matrix: - allow_failures: - - os: osx +#- osx +#matrix: +# allow_failures: +# - os: osx dist: trusty before_install: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then @@ -26,7 +26,7 @@ before_install: script: - make clean && make update && - make build/harddrive.bin.gz build/livedisk.bin.gz build/livedisk.iso -j 2 + make build/harddrive.bin.gz build/livedisk.bin.gz build/livedisk.iso notifications: email: false webhooks: http://37.139.9.28:54863/travis From 7ae998ce3b592953fbca69e264998bc5b861d3db Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 5 Jan 2017 11:22:36 -0700 Subject: [PATCH 4/6] Move drivers to submodule --- .gitmodules | 3 + drivers | 1 + drivers/ahcid/Cargo.toml | 10 - drivers/ahcid/src/ahci/disk.rs | 108 ------- drivers/ahcid/src/ahci/fis.rs | 155 ----------- drivers/ahcid/src/ahci/hba.rs | 417 ---------------------------- drivers/ahcid/src/ahci/mod.rs | 35 --- drivers/ahcid/src/main.rs | 86 ------ drivers/ahcid/src/scheme.rs | 166 ----------- drivers/bgad/Cargo.toml | 3 - drivers/bgad/src/main.rs | 13 - drivers/e1000d/Cargo.toml | 11 - drivers/e1000d/src/device.rs | 346 ----------------------- drivers/e1000d/src/main.rs | 136 --------- drivers/pcid/Cargo.toml | 8 - drivers/pcid/src/config.rs | 14 - drivers/pcid/src/main.rs | 160 ----------- drivers/pcid/src/pci/bar.rs | 18 -- drivers/pcid/src/pci/bus.rs | 46 --- drivers/pcid/src/pci/class.rs | 50 ---- drivers/pcid/src/pci/dev.rs | 46 --- drivers/pcid/src/pci/func.rs | 30 -- drivers/pcid/src/pci/header.rs | 43 --- drivers/pcid/src/pci/mod.rs | 78 ------ drivers/ps2d/Cargo.toml | 10 - drivers/ps2d/src/controller.rs | 233 ---------------- drivers/ps2d/src/keymap.rs | 148 ---------- drivers/ps2d/src/main.rs | 193 ------------- drivers/rtl8168d/Cargo.toml | 11 - drivers/rtl8168d/src/device.rs | 300 -------------------- drivers/rtl8168d/src/main.rs | 141 ---------- drivers/vesad/Cargo.toml | 12 - drivers/vesad/src/display.rs | 246 ---------------- drivers/vesad/src/main.rs | 109 -------- drivers/vesad/src/mode_info.rs | 37 --- drivers/vesad/src/primitive.rs | 47 ---- drivers/vesad/src/scheme.rs | 190 ------------- drivers/vesad/src/screen/graphic.rs | 126 --------- drivers/vesad/src/screen/mod.rs | 32 --- drivers/vesad/src/screen/text.rs | 233 ---------------- 40 files changed, 4 insertions(+), 4047 deletions(-) create mode 160000 drivers delete mode 100644 drivers/ahcid/Cargo.toml delete mode 100644 drivers/ahcid/src/ahci/disk.rs delete mode 100644 drivers/ahcid/src/ahci/fis.rs delete mode 100644 drivers/ahcid/src/ahci/hba.rs delete mode 100644 drivers/ahcid/src/ahci/mod.rs delete mode 100644 drivers/ahcid/src/main.rs delete mode 100644 drivers/ahcid/src/scheme.rs delete mode 100644 drivers/bgad/Cargo.toml delete mode 100644 drivers/bgad/src/main.rs delete mode 100644 drivers/e1000d/Cargo.toml delete mode 100644 drivers/e1000d/src/device.rs delete mode 100644 drivers/e1000d/src/main.rs delete mode 100644 drivers/pcid/Cargo.toml delete mode 100644 drivers/pcid/src/config.rs delete mode 100644 drivers/pcid/src/main.rs delete mode 100644 drivers/pcid/src/pci/bar.rs delete mode 100644 drivers/pcid/src/pci/bus.rs delete mode 100644 drivers/pcid/src/pci/class.rs delete mode 100644 drivers/pcid/src/pci/dev.rs delete mode 100644 drivers/pcid/src/pci/func.rs delete mode 100644 drivers/pcid/src/pci/header.rs delete mode 100644 drivers/pcid/src/pci/mod.rs delete mode 100644 drivers/ps2d/Cargo.toml delete mode 100644 drivers/ps2d/src/controller.rs delete mode 100644 drivers/ps2d/src/keymap.rs delete mode 100644 drivers/ps2d/src/main.rs delete mode 100644 drivers/rtl8168d/Cargo.toml delete mode 100644 drivers/rtl8168d/src/device.rs delete mode 100644 drivers/rtl8168d/src/main.rs delete mode 100644 drivers/vesad/Cargo.toml delete mode 100644 drivers/vesad/src/display.rs delete mode 100644 drivers/vesad/src/main.rs delete mode 100644 drivers/vesad/src/mode_info.rs delete mode 100644 drivers/vesad/src/primitive.rs delete mode 100644 drivers/vesad/src/scheme.rs delete mode 100644 drivers/vesad/src/screen/graphic.rs delete mode 100644 drivers/vesad/src/screen/mod.rs delete mode 100644 drivers/vesad/src/screen/text.rs diff --git a/.gitmodules b/.gitmodules index 02f5947..c5a24df 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,3 +61,6 @@ [submodule "kernel"] path = kernel url = https://github.com/redox-os/kernel.git +[submodule "drivers"] + path = drivers + url = https://github.com/redox-os/drivers.git diff --git a/drivers b/drivers new file mode 160000 index 0000000..9eeade8 --- /dev/null +++ b/drivers @@ -0,0 +1 @@ +Subproject commit 9eeade88606095a03ee3c285972b45d1c499a971 diff --git a/drivers/ahcid/Cargo.toml b/drivers/ahcid/Cargo.toml deleted file mode 100644 index f718331..0000000 --- a/drivers/ahcid/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "ahcid" -version = "0.1.0" - -[dependencies] -bitflags = "*" -dma = { path = "../../crates/dma/" } -io = { path = "../../crates/io/" } -spin = "*" -redox_syscall = { path = "../../syscall/" } diff --git a/drivers/ahcid/src/ahci/disk.rs b/drivers/ahcid/src/ahci/disk.rs deleted file mode 100644 index 4389425..0000000 --- a/drivers/ahcid/src/ahci/disk.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::ptr; - -use dma::Dma; -use syscall::error::Result; - -use super::hba::{HbaPort, HbaCmdTable, HbaCmdHeader}; - -pub struct Disk { - id: usize, - port: &'static mut HbaPort, - size: u64, - clb: Dma<[HbaCmdHeader; 32]>, - ctbas: [Dma; 32], - _fb: Dma<[u8; 256]>, - buf: Dma<[u8; 256 * 512]> -} - -impl Disk { - pub fn new(id: usize, port: &'static mut HbaPort) -> Result { - let mut clb = Dma::zeroed()?; - let mut ctbas = [ - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - ]; - let mut fb = Dma::zeroed()?; - let buf = Dma::zeroed()?; - - port.init(&mut clb, &mut ctbas, &mut fb); - - let size = unsafe { port.identify(&mut clb, &mut ctbas).unwrap_or(0) }; - - Ok(Disk { - id: id, - port: port, - size: size, - clb: clb, - ctbas: ctbas, - _fb: fb, - buf: buf - }) - } - - pub fn id(&self) -> usize { - self.id - } - - pub fn size(&self) -> u64 { - self.size - } - - pub fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result { - let sectors = buffer.len()/512; - - let mut sector: usize = 0; - while sectors - sector >= 255 { - if let Err(err) = self.port.ata_dma(block + sector as u64, 255, false, &mut self.clb, &mut self.ctbas, &mut self.buf) { - return Err(err); - } - - unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), 255 * 512); } - - sector += 255; - } - if sector < sectors { - if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, false, &mut self.clb, &mut self.ctbas, &mut self.buf) { - return Err(err); - } - - unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), (sectors - sector) * 512); } - - sector += sectors - sector; - } - - Ok(sector * 512) - } - - pub fn write(&mut self, block: u64, buffer: &[u8]) -> Result { - let sectors = buffer.len()/512; - - let mut sector: usize = 0; - while sectors - sector >= 255 { - unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), 255 * 512); } - - if let Err(err) = self.port.ata_dma(block + sector as u64, 255, true, &mut self.clb, &mut self.ctbas, &mut self.buf) { - return Err(err); - } - - sector += 255; - } - if sector < sectors { - unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), (sectors - sector) * 512); } - - if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, true, &mut self.clb, &mut self.ctbas, &mut self.buf) { - return Err(err); - } - - sector += sectors - sector; - } - - Ok(sector * 512) - } -} diff --git a/drivers/ahcid/src/ahci/fis.rs b/drivers/ahcid/src/ahci/fis.rs deleted file mode 100644 index 7dbe33c..0000000 --- a/drivers/ahcid/src/ahci/fis.rs +++ /dev/null @@ -1,155 +0,0 @@ -use io::Mmio; - -#[repr(u8)] -pub enum FisType { - /// Register FIS - host to device - RegH2D = 0x27, - /// Register FIS - device to host - RegD2H = 0x34, - /// DMA activate FIS - device to host - DmaAct = 0x39, - /// DMA setup FIS - bidirectional - DmaSetup = 0x41, - /// Data FIS - bidirectional - Data = 0x46, - /// BIST activate FIS - bidirectional - Bist = 0x58, - /// PIO setup FIS - device to host - PioSetup = 0x5F, - /// Set device bits FIS - device to host - DevBits = 0xA1 -} - -#[repr(packed)] -pub struct FisRegH2D { - // DWORD 0 - pub fis_type: Mmio, // FIS_TYPE_REG_H2D - - pub pm: Mmio, // Port multiplier, 1: Command, 0: Control - - pub command: Mmio, // Command register - pub featurel: Mmio, // Feature register, 7:0 - - // DWORD 1 - pub lba0: Mmio, // LBA low register, 7:0 - pub lba1: Mmio, // LBA mid register, 15:8 - pub lba2: Mmio, // LBA high register, 23:16 - pub device: Mmio, // Device register - - // DWORD 2 - pub lba3: Mmio, // LBA register, 31:24 - pub lba4: Mmio, // LBA register, 39:32 - pub lba5: Mmio, // LBA register, 47:40 - pub featureh: Mmio, // Feature register, 15:8 - - // DWORD 3 - pub countl: Mmio, // Count register, 7:0 - pub counth: Mmio, // Count register, 15:8 - pub icc: Mmio, // Isochronous command completion - pub control: Mmio, // Control register - - // DWORD 4 - pub rsv1: [Mmio; 4], // Reserved -} - -#[repr(packed)] -pub struct FisRegD2H { - // DWORD 0 - pub fis_type: Mmio, // FIS_TYPE_REG_D2H - - pub pm: Mmio, // Port multiplier, Interrupt bit: 2 - - pub status: Mmio, // Status register - pub error: Mmio, // Error register - - // DWORD 1 - pub lba0: Mmio, // LBA low register, 7:0 - pub lba1: Mmio, // LBA mid register, 15:8 - pub lba2: Mmio, // LBA high register, 23:16 - pub device: Mmio, // Device register - - // DWORD 2 - pub lba3: Mmio, // LBA register, 31:24 - pub lba4: Mmio, // LBA register, 39:32 - pub lba5: Mmio, // LBA register, 47:40 - pub rsv2: Mmio, // Reserved - - // DWORD 3 - pub countl: Mmio, // Count register, 7:0 - pub counth: Mmio, // Count register, 15:8 - pub rsv3: [Mmio; 2], // Reserved - - // DWORD 4 - pub rsv4: [Mmio; 4], // Reserved -} - -#[repr(packed)] -pub struct FisData { - // DWORD 0 - pub fis_type: Mmio, // FIS_TYPE_DATA - - pub pm: Mmio, // Port multiplier - - pub rsv1: [Mmio; 2], // Reserved - - // DWORD 1 ~ N - pub data: [Mmio; 252], // Payload -} - -#[repr(packed)] -pub struct FisPioSetup { - // DWORD 0 - pub fis_type: Mmio, // FIS_TYPE_PIO_SETUP - - pub pm: Mmio, // Port multiplier, direction: 4 - device to host, interrupt: 2 - - pub status: Mmio, // Status register - pub error: Mmio, // Error register - - // DWORD 1 - pub lba0: Mmio, // LBA low register, 7:0 - pub lba1: Mmio, // LBA mid register, 15:8 - pub lba2: Mmio, // LBA high register, 23:16 - pub device: Mmio, // Device register - - // DWORD 2 - pub lba3: Mmio, // LBA register, 31:24 - pub lba4: Mmio, // LBA register, 39:32 - pub lba5: Mmio, // LBA register, 47:40 - pub rsv2: Mmio, // Reserved - - // DWORD 3 - pub countl: Mmio, // Count register, 7:0 - pub counth: Mmio, // Count register, 15:8 - pub rsv3: Mmio, // Reserved - pub e_status: Mmio, // New value of status register - - // DWORD 4 - pub tc: Mmio, // Transfer count - pub rsv4: [Mmio; 2], // Reserved -} - -#[repr(packed)] -pub struct FisDmaSetup { - // DWORD 0 - pub fis_type: Mmio, // FIS_TYPE_DMA_SETUP - - pub pm: Mmio, // Port multiplier, direction: 4 - device to host, interrupt: 2, auto-activate: 1 - - pub rsv1: [Mmio; 2], // Reserved - - // DWORD 1&2 - pub dma_buffer_id: Mmio, /* DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */ - - // DWORD 3 - pub rsv3: Mmio, // More reserved - - // DWORD 4 - pub dma_buffer_offset: Mmio, // Byte offset into buffer. First 2 bits must be 0 - - // DWORD 5 - pub transfer_count: Mmio, // Number of bytes to transfer. Bit 0 must be 0 - - // DWORD 6 - pub rsv6: Mmio, // Reserved -} diff --git a/drivers/ahcid/src/ahci/hba.rs b/drivers/ahcid/src/ahci/hba.rs deleted file mode 100644 index d22b706..0000000 --- a/drivers/ahcid/src/ahci/hba.rs +++ /dev/null @@ -1,417 +0,0 @@ -use std::mem::size_of; -use std::ops::DerefMut; -use std::{ptr, u32}; - -use dma::Dma; -use io::{Io, Mmio}; -use syscall::error::{Error, Result, EIO}; - -use super::fis::{FisType, FisRegH2D}; - -const ATA_CMD_READ_DMA_EXT: u8 = 0x25; -const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35; -const ATA_CMD_IDENTIFY: u8 = 0xEC; -const ATA_DEV_BUSY: u8 = 0x80; -const ATA_DEV_DRQ: u8 = 0x08; - -const HBA_PORT_CMD_CR: u32 = 1 << 15; -const HBA_PORT_CMD_FR: u32 = 1 << 14; -const HBA_PORT_CMD_FRE: u32 = 1 << 4; -const HBA_PORT_CMD_ST: u32 = 1; -const HBA_PORT_IS_ERR: u32 = 1 << 30 | 1 << 29 | 1 << 28 | 1 << 27; -const HBA_SSTS_PRESENT: u32 = 0x3; -const HBA_SIG_ATA: u32 = 0x00000101; -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, - Unknown(u32), - SATA, - SATAPI, - PM, - SEMB, -} - -#[repr(packed)] -pub struct HbaPort { - pub clb: [Mmio; 2], // 0x00, command list base address, 1K-byte aligned - pub fb: [Mmio; 2], // 0x08, FIS base address, 256-byte aligned - pub is: Mmio, // 0x10, interrupt status - pub ie: Mmio, // 0x14, interrupt enable - pub cmd: Mmio, // 0x18, command and status - pub _rsv0: Mmio, // 0x1C, Reserved - pub tfd: Mmio, // 0x20, task file data - pub sig: Mmio, // 0x24, signature - pub ssts: Mmio, // 0x28, SATA status (SCR0:SStatus) - pub sctl: Mmio, // 0x2C, SATA control (SCR2:SControl) - pub serr: Mmio, // 0x30, SATA error (SCR1:SError) - pub sact: Mmio, // 0x34, SATA active (SCR3:SActive) - pub ci: Mmio, // 0x38, command issue - pub sntf: Mmio, // 0x3C, SATA notification (SCR4:SNotification) - pub fbs: Mmio, // 0x40, FIS-based switch control - pub _rsv1: [Mmio; 11], // 0x44 ~ 0x6F, Reserved - pub vendor: [Mmio; 4], // 0x70 ~ 0x7F, vendor specific -} - -impl HbaPort { - pub fn probe(&self) -> HbaPortType { - if self.ssts.readf(HBA_SSTS_PRESENT) { - let sig = self.sig.read(); - match sig { - HBA_SIG_ATA => HbaPortType::SATA, - HBA_SIG_ATAPI => HbaPortType::SATAPI, - HBA_SIG_PM => HbaPortType::PM, - HBA_SIG_SEMB => HbaPortType::SEMB, - _ => HbaPortType::Unknown(sig), - } - } else { - HbaPortType::None - } - } - - pub fn start(&mut self) { - while self.cmd.readf(HBA_PORT_CMD_CR) { - pause(); - } - - self.cmd.writef(HBA_PORT_CMD_FRE | HBA_PORT_CMD_ST, true); - } - - 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) { - pause(); - } - - self.cmd.writef(HBA_PORT_CMD_FRE, false); - } - - pub fn slot(&self) -> Option { - let slots = self.sact.read() | self.ci.read(); - for i in 0..32 { - if slots & 1 << i == 0 { - return Some(i); - } - } - None - } - - pub fn init(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma; 32], fb: &mut Dma<[u8; 256]>) { - self.stop(); - - for i in 0..32 { - let cmdheader = &mut clb[i]; - cmdheader.ctba.write(ctbas[i].physical() as u64); - cmdheader.prdtl.write(0); - } - - self.clb[0].write(clb.physical() as u32); - self.clb[1].write((clb.physical() >> 32) as u32); - self.fb[0].write(fb.physical() as u32); - self.fb[1].write((fb.physical() >> 32) as u32); - let is = self.is.read(); - self.is.write(is); - self.ie.write(0); - let serr = self.serr.read(); - self.serr.write(serr); - - // Disable power management - let sctl = self.sctl.read() ; - self.sctl.write(sctl | 7 << 8); - - // Power on and spin up device - self.cmd.writef(1 << 2 | 1 << 1, true); - - print!("{}", format!(" - AHCI init {:X}\n", self.cmd.read())); - } - - pub unsafe fn identify(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma; 32]) -> Option { - self.is.write(u32::MAX); - - let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap(); - - if let Some(slot) = self.slot() { - let cmdheader = &mut clb[slot as usize]; - cmdheader.cfl.write(((size_of::() / size_of::()) as u8)); - cmdheader.prdtl.write(1); - - { - let cmdtbl = &mut ctbas[slot as usize]; - ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::()); - - let prdt_entry = &mut cmdtbl.prdt_entry[0]; - prdt_entry.dba.write(dest.physical() as u64); - prdt_entry.dbc.write(512 | 1); - } - - { - let cmdfis = &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D); - - cmdfis.fis_type.write(FisType::RegH2D as u8); - cmdfis.pm.write(1 << 7); - cmdfis.command.write(ATA_CMD_IDENTIFY); - cmdfis.device.write(0); - cmdfis.countl.write(1); - cmdfis.counth.write(0); - } - - while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) { - pause(); - } - - self.ci.writef(1 << slot, true); - - self.start(); - - while (self.ci.readf(1 << slot) || self.tfd.readf(0x80)) && self.is.read() & HBA_PORT_IS_ERR == 0 { - pause(); - } - - self.stop(); - - if self.is.read() & HBA_PORT_IS_ERR != 0 { - print!("{}", format!("ERROR IS {:X} TFD {:X} SERR {:X}\n", self.is.read(), self.tfd.read(), self.serr.read())); - return None; - } - - let mut serial = String::new(); - for word in 10..20 { - let d = dest[word]; - let a = ((d >> 8) as u8) as char; - if a != '\0' { - serial.push(a); - } - let b = (d as u8) as char; - if b != '\0' { - serial.push(b); - } - } - - let mut firmware = String::new(); - for word in 23..27 { - let d = dest[word]; - let a = ((d >> 8) as u8) as char; - if a != '\0' { - firmware.push(a); - } - let b = (d as u8) as char; - if b != '\0' { - firmware.push(b); - } - } - - let mut model = String::new(); - for word in 27..47 { - let d = dest[word]; - let a = ((d >> 8) as u8) as char; - if a != '\0' { - model.push(a); - } - let b = (d as u8) as char; - if b != '\0' { - model.push(b); - } - } - - let mut sectors = (dest[100] as u64) | - ((dest[101] as u64) << 16) | - ((dest[102] as u64) << 32) | - ((dest[103] as u64) << 48); - - let lba_bits = if sectors == 0 { - sectors = (dest[60] as u64) | ((dest[61] as u64) << 16); - 28 - } else { - 48 - }; - - print!("{}", format!(" + Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB\n", - serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048)); - - Some(sectors * 512) - } else { - None - } - } - - pub fn ata_dma(&mut self, block: u64, sectors: usize, write: bool, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma; 32], buf: &mut Dma<[u8; 256 * 512]>) -> Result { - if write { - //print!("{}", format!("AHCI {:X} DMA BLOCK: {:X} SECTORS: {} WRITE: {}\n", (self as *mut HbaPort) as usize, block, sectors, write)); - } - - assert!(sectors > 0 && sectors < 256); - - self.is.write(u32::MAX); - - if let Some(slot) = self.slot() { - if write { - //print!("{}", format!("SLOT {}\n", slot)); - } - - let cmdheader = &mut clb[slot as usize]; - - cmdheader.cfl.write(((size_of::() / size_of::()) as u8) | if write { 1 << 7 | 1 << 6 } else { 0 }); - - cmdheader.prdtl.write(1); - - { - let cmdtbl = &mut ctbas[slot as usize]; - unsafe { ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::()) }; - - let prdt_entry = &mut cmdtbl.prdt_entry[0]; - prdt_entry.dba.write(buf.physical() as u64); - prdt_entry.dbc.write(((sectors * 512) as u32) | 1); - } - - { - let cmdfis = unsafe { &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D) }; - - cmdfis.fis_type.write(FisType::RegH2D as u8); - cmdfis.pm.write(1 << 7); - if write { - cmdfis.command.write(ATA_CMD_WRITE_DMA_EXT); - } else { - cmdfis.command.write(ATA_CMD_READ_DMA_EXT); - } - - cmdfis.lba0.write(block as u8); - cmdfis.lba1.write((block >> 8) as u8); - cmdfis.lba2.write((block >> 16) as u8); - - cmdfis.device.write(1 << 6); - - cmdfis.lba3.write((block >> 24) as u8); - cmdfis.lba4.write((block >> 32) as u8); - cmdfis.lba5.write((block >> 40) as u8); - - cmdfis.countl.write(sectors as u8); - cmdfis.counth.write((sectors >> 8) as u8); - } - - if write { - //print!("WAIT ATA_DEV_BUSY | ATA_DEV_DRQ\n"); - } - while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) { - pause(); - } - - if write { - //print!("{}", format!("WRITE CI {:X} in {:X}\n", 1 << slot, self.ci.read())); - } - self.ci.writef(1 << slot, true); - - self.start(); - - if write { - //print!("{}", format!("WAIT CI {:X} in {:X}\n", 1 << slot, self.ci.read())); - } - while (self.ci.readf(1 << slot) || self.tfd.readf(0x80)) && self.is.read() & HBA_PORT_IS_ERR == 0 { - pause(); - if write { - //print!("{}", format!("WAIT CI {:X} TFD {:X} IS {:X} CMD {:X} SERR {:X}\n", self.ci.read(), self.tfd.read(), self.is.read(), self.cmd.read(), self.serr.read())); - } - } - - self.stop(); - - if self.is.read() & HBA_PORT_IS_ERR != 0 { - print!("{}", format!("ERROR IS {:X} IE {:X} CMD {:X} TFD {:X}\nSSTS {:X} SCTL {:X} SERR {:X} SACT {:X}\nCI {:X} SNTF {:X} FBS {:X}\n", - self.is.read(), self.ie.read(), self.cmd.read(), self.tfd.read(), - self.ssts.read(), self.sctl.read(), self.serr.read(), self.sact.read(), - self.ci.read(), self.sntf.read(), self.fbs.read())); - self.is.write(u32::MAX); - return Err(Error::new(EIO)); - } - - if write { - //print!("{}", format!("SUCCESS {}\n", sectors)); - } - Ok(sectors * 512) - } else { - print!("No Command Slots\n"); - Err(Error::new(EIO)) - } - } -} - -#[repr(packed)] -pub struct HbaMem { - pub cap: Mmio, // 0x00, Host capability - pub ghc: Mmio, // 0x04, Global host control - pub is: Mmio, // 0x08, Interrupt status - pub pi: Mmio, // 0x0C, Port implemented - pub vs: Mmio, // 0x10, Version - pub ccc_ctl: Mmio, // 0x14, Command completion coalescing control - pub ccc_pts: Mmio, // 0x18, Command completion coalescing ports - pub em_loc: Mmio, // 0x1C, Enclosure management location - pub em_ctl: Mmio, // 0x20, Enclosure management control - pub cap2: Mmio, // 0x24, Host capabilities extended - pub bohc: Mmio, // 0x28, BIOS/OS handoff control and status - pub _rsv: [Mmio; 116], // 0x2C - 0x9F, Reserved - pub vendor: [Mmio; 96], // 0xA0 - 0xFF, Vendor specific registers - pub ports: [HbaPort; 32], // 0x100 - 0x10FF, Port control registers -} - -impl HbaMem { - pub fn init(&mut self) { - /* - self.ghc.writef(1, true); - while self.ghc.readf(1) { - pause(); - } - */ - self.ghc.write(1 << 31 | 1 << 1); - - print!("{}", format!(" - AHCI CAP {:X} GHC {:X} IS {:X} PI {:X} VS {:X} CAP2 {:X} BOHC {:X}", - self.cap.read(), self.ghc.read(), self.is.read(), self.pi.read(), - self.vs.read(), self.cap2.read(), self.bohc.read())); - } -} - -#[repr(packed)] -pub struct HbaPrdtEntry { - dba: Mmio, // Data base address - _rsv0: Mmio, // Reserved - dbc: Mmio, // Byte count, 4M max, interrupt = 1 -} - -#[repr(packed)] -pub struct HbaCmdTable { - // 0x00 - cfis: [Mmio; 64], // Command FIS - - // 0x40 - _acmd: [Mmio; 16], // ATAPI command, 12 or 16 bytes - - // 0x50 - _rsv: [Mmio; 48], // Reserved - - // 0x80 - prdt_entry: [HbaPrdtEntry; 65536], // Physical region descriptor table entries, 0 ~ 65535 -} - -#[repr(packed)] -pub struct HbaCmdHeader { - // DW0 - cfl: Mmio, /* Command FIS length in DWORDS, 2 ~ 16, atapi: 4, write - host to device: 2, prefetchable: 1 */ - _pm: Mmio, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier - - prdtl: Mmio, // Physical region descriptor table length in entries - - // DW1 - _prdbc: Mmio, // Physical region descriptor byte count transferred - - // DW2, 3 - ctba: Mmio, // Command table descriptor base address - - // DW4 - 7 - _rsv1: [Mmio; 4], // Reserved -} diff --git a/drivers/ahcid/src/ahci/mod.rs b/drivers/ahcid/src/ahci/mod.rs deleted file mode 100644 index fec9e00..0000000 --- a/drivers/ahcid/src/ahci/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -use io::Io; - -use self::disk::Disk; -use self::hba::{HbaMem, HbaPortType}; - -pub mod disk; -pub mod fis; -pub mod hba; - -pub fn disks(base: usize, name: &str) -> Vec { - unsafe { &mut *(base as *mut HbaMem) }.init(); - let pi = unsafe { &mut *(base as *mut HbaMem) }.pi.read(); - let ret: Vec = (0..32) - .filter(|&i| pi & 1 << i as i32 == 1 << i as i32) - .filter_map(|i| { - let port = &mut unsafe { &mut *(base as *mut HbaMem) }.ports[i]; - let port_type = port.probe(); - print!("{}", format!("{}-{}: {:?}\n", name, i, port_type)); - match port_type { - HbaPortType::SATA => { - match Disk::new(i, port) { - Ok(disk) => Some(disk), - Err(err) => { - print!("{}", format!("{}: {}\n", i, err)); - None - } - } - } - _ => None, - } - }) - .collect(); - - ret -} diff --git a/drivers/ahcid/src/main.rs b/drivers/ahcid/src/main.rs deleted file mode 100644 index 4e8cbc9..0000000 --- a/drivers/ahcid/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![feature(asm)] - -#[macro_use] -extern crate bitflags; -extern crate dma; -extern crate io; -extern crate spin; -extern crate syscall; - -use std::{env, usize}; -use std::fs::File; -use std::io::{Read, Write}; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use syscall::{EVENT_READ, MAP_WRITE, Event, Packet, Result, Scheme}; - -use scheme::DiskScheme; - -pub mod ahci; -pub mod scheme; - -fn create_scheme_fallback<'a>(name: &'a str, fallback: &'a str) -> Result<(&'a str, RawFd)> { - if let Ok(fd) = syscall::open(&format!(":{}", name), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK) { - Ok((name, fd)) - } else { - syscall::open(&format!(":{}", fallback), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK) - .map(|fd| (fallback, fd)) - } -} - -fn main() { - let mut args = env::args().skip(1); - - let mut name = args.next().expect("ahcid: no name provided"); - name.push_str("_ahci"); - - let bar_str = args.next().expect("ahcid: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("ahcid: failed to parse address"); - - let irq_str = args.next().expect("ahcid: no irq provided"); - let irq = irq_str.parse::().expect("ahcid: failed to parse irq"); - - print!("{}", format!(" + AHCI {} on: {:X} IRQ: {}\n", name, bar, irq)); - - // Daemonize - if unsafe { syscall::clone(0).unwrap() } == 0 { - let address = unsafe { syscall::physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") }; - { - let (_scheme_name, socket_fd) = create_scheme_fallback("disk", &name).expect("ahcid: failed to create disk scheme"); - let mut socket = unsafe { File::from_raw_fd(socket_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, &name)); - loop { - let mut event = Event::default(); - if event_file.read(&mut event).expect("ahcid: failed to read event file") == 0 { - break; - } - if event.id == socket_fd { - loop { - let mut packet = Packet::default(); - if socket.read(&mut packet).expect("ahcid: failed to read disk scheme") == 0 { - break; - } - 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() { - //TODO : Test for IRQ - //irq_file.write(&irq).expect("ahcid: failed to write irq file"); - } - } else { - println!("Unknown event {}", event.id); - } - } - } - unsafe { let _ = syscall::physunmap(address); } - } -} diff --git a/drivers/ahcid/src/scheme.rs b/drivers/ahcid/src/scheme.rs deleted file mode 100644 index cd35d76..0000000 --- a/drivers/ahcid/src/scheme.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::collections::BTreeMap; -use std::{cmp, str}; -use std::fmt::Write; -use std::io::Read; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use spin::Mutex; -use syscall::{Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, Result, Scheme, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT, SEEK_CUR, SEEK_END, SEEK_SET}; - -use ahci::disk::Disk; - -#[derive(Clone)] -enum Handle { - //TODO: Make these enum variants normal tuples (), not nested tuples (()) - List((Vec, usize)), - Disk((Arc>, usize)) -} - -pub struct DiskScheme { - disks: Box<[Arc>]>, - handles: Mutex>, - next_id: AtomicUsize -} - -impl DiskScheme { - pub fn new(disks: Vec) -> DiskScheme { - let mut disk_arcs = vec![]; - for disk in disks { - disk_arcs.push(Arc::new(Mutex::new(disk))); - } - - DiskScheme { - disks: disk_arcs.into_boxed_slice(), - handles: Mutex::new(BTreeMap::new()), - next_id: AtomicUsize::new(0) - } - } -} - -impl Scheme for DiskScheme { - 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)))?.trim_matches('/'); - if path_str.is_empty() { - if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT { - let mut list = String::new(); - - for i in 0..self.disks.len() { - write!(list, "{}\n", i).unwrap(); - } - - let id = self.next_id.fetch_add(1, Ordering::SeqCst); - self.handles.lock().insert(id, Handle::List((list.into_bytes(), 0))); - Ok(id) - } else { - Err(Error::new(EISDIR)) - } - } else { - 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, Handle::Disk((disk.clone(), 0))); - Ok(id) - } else { - Err(Error::new(ENOENT)) - } - } - } else { - Err(Error::new(EACCES)) - } - } - - fn dup(&self, id: usize, _buf: &[u8]) -> Result { - let mut handles = self.handles.lock(); - let new_handle = { - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - let new_id = self.next_id.fetch_add(1, Ordering::SeqCst); - handles.insert(new_id, new_handle); - Ok(new_id) - } - - fn fstat(&self, id: usize, stat: &mut Stat) -> Result { - let handles = self.handles.lock(); - match *handles.get(&id).ok_or(Error::new(EBADF))? { - Handle::List(ref handle) => { - stat.st_mode = MODE_DIR; - stat.st_size = handle.0.len() as u64; - Ok(0) - }, - Handle::Disk(ref handle) => { - stat.st_mode = MODE_FILE; - stat.st_size = handle.0.lock().size(); - Ok(0) - } - } - } - - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let mut handles = self.handles.lock(); - match *handles.get_mut(&id).ok_or(Error::new(EBADF))? { - Handle::List(ref mut handle) => { - let count = (&handle.0[handle.1..]).read(buf).unwrap(); - handle.1 += count; - Ok(count) - }, - Handle::Disk(ref mut handle) => { - let mut disk = handle.0.lock(); - let count = disk.read((handle.1 as u64)/512, buf)?; - handle.1 += count; - Ok(count) - } - } - } - - fn write(&self, id: usize, buf: &[u8]) -> Result { - let mut handles = self.handles.lock(); - match *handles.get_mut(&id).ok_or(Error::new(EBADF))? { - Handle::List(_) => { - Err(Error::new(EBADF)) - }, - Handle::Disk(ref mut handle) => { - let mut disk = handle.0.lock(); - let count = disk.write((handle.1 as u64)/512, buf)?; - handle.1 += count; - Ok(count) - } - } - } - - fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { - let mut handles = self.handles.lock(); - match *handles.get_mut(&id).ok_or(Error::new(EBADF))? { - Handle::List(ref mut handle) => { - let len = handle.0.len() as usize; - handle.1 = match whence { - SEEK_SET => cmp::min(len, pos), - SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.1 as isize + pos as isize)) as usize, - SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, - _ => return Err(Error::new(EINVAL)) - }; - - Ok(handle.1) - }, - Handle::Disk(ref mut handle) => { - let len = handle.0.lock().size() as usize; - handle.1 = match whence { - SEEK_SET => cmp::min(len, pos), - SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.1 as isize + pos as isize)) as usize, - SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, - _ => return Err(Error::new(EINVAL)) - }; - - Ok(handle.1) - } - } - } - - fn close(&self, id: usize) -> Result { - let mut handles = self.handles.lock(); - handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) - } -} diff --git a/drivers/bgad/Cargo.toml b/drivers/bgad/Cargo.toml deleted file mode 100644 index c763e32..0000000 --- a/drivers/bgad/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[package] -name = "bgad" -version = "0.1.0" diff --git a/drivers/bgad/src/main.rs b/drivers/bgad/src/main.rs deleted file mode 100644 index 48d75d7..0000000 --- a/drivers/bgad/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::env; - -fn main() { - let mut args = env::args().skip(1); - - let mut name = args.next().expect("bgad: no name provided"); - name.push_str("_bga"); - - let bar_str = args.next().expect("bgad: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("bgad: failed to parse address"); - - print!("{}", format!(" + BGA {} on: {:X}\n", name, bar)); -} diff --git a/drivers/e1000d/Cargo.toml b/drivers/e1000d/Cargo.toml deleted file mode 100644 index ec8c45e..0000000 --- a/drivers/e1000d/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "e1000d" -version = "0.1.0" - -[dependencies] -bitflags = "*" -dma = { path = "../../crates/dma/" } -event = { path = "../../crates/event/" } -io = { path = "../../crates/io/" } -netutils = { path = "../../programs/netutils/" } -redox_syscall = { path = "../../syscall/" } diff --git a/drivers/e1000d/src/device.rs b/drivers/e1000d/src/device.rs deleted file mode 100644 index 8cb46f3..0000000 --- a/drivers/e1000d/src/device.rs +++ /dev/null @@ -1,346 +0,0 @@ -use std::{cmp, mem, ptr, slice}; - -use dma::Dma; -use netutils::setcfg; -use syscall::error::{Error, EACCES, EWOULDBLOCK, Result}; -use syscall::flag::O_NONBLOCK; -use syscall::scheme::Scheme; - -const CTRL: u32 = 0x00; -const CTRL_LRST: u32 = 1 << 3; -const CTRL_ASDE: u32 = 1 << 5; -const CTRL_SLU: u32 = 1 << 6; -const CTRL_ILOS: u32 = 1 << 7; -const CTRL_VME: u32 = 1 << 30; -const CTRL_PHY_RST: u32 = 1 << 31; - -const STATUS: u32 = 0x08; - -const FCAL: u32 = 0x28; -const FCAH: u32 = 0x2C; -const FCT: u32 = 0x30; -const FCTTV: u32 = 0x170; - -const ICR: u32 = 0xC0; - -const IMS: u32 = 0xD0; -const IMS_TXDW: u32 = 1; -const IMS_TXQE: u32 = 1 << 1; -const IMS_LSC: u32 = 1 << 2; -const IMS_RXSEQ: u32 = 1 << 3; -const IMS_RXDMT: u32 = 1 << 4; -const IMS_RX: u32 = 1 << 6; -const IMS_RXT: u32 = 1 << 7; - -const RCTL: u32 = 0x100; -const RCTL_EN: u32 = 1 << 1; -const RCTL_UPE: u32 = 1 << 3; -const RCTL_MPE: u32 = 1 << 4; -const RCTL_LPE: u32 = 1 << 5; -const RCTL_LBM: u32 = 1 << 6 | 1 << 7; -const RCTL_BAM: u32 = 1 << 15; -const RCTL_BSIZE1: u32 = 1 << 16; -const RCTL_BSIZE2: u32 = 1 << 17; -const RCTL_BSEX: u32 = 1 << 25; -const RCTL_SECRC: u32 = 1 << 26; - -const RDBAL: u32 = 0x2800; -const RDBAH: u32 = 0x2804; -const RDLEN: u32 = 0x2808; -const RDH: u32 = 0x2810; -const RDT: u32 = 0x2818; - -const RAL0: u32 = 0x5400; -const RAH0: u32 = 0x5404; - -#[derive(Debug)] -#[repr(packed)] -struct Rd { - buffer: u64, - length: u16, - checksum: u16, - status: u8, - error: u8, - special: u16, -} -const RD_DD: u8 = 1; -const RD_EOP: u8 = 1 << 1; - -const TCTL: u32 = 0x400; -const TCTL_EN: u32 = 1 << 1; -const TCTL_PSP: u32 = 1 << 3; - -const TDBAL: u32 = 0x3800; -const TDBAH: u32 = 0x3804; -const TDLEN: u32 = 0x3808; -const TDH: u32 = 0x3810; -const TDT: u32 = 0x3818; - -#[derive(Debug)] -#[repr(packed)] -struct Td { - buffer: u64, - length: u16, - cso: u8, - command: u8, - status: u8, - css: u8, - special: u16, -} -const TD_CMD_EOP: u8 = 1; -const TD_CMD_IFCS: u8 = 1 << 1; -const TD_CMD_RS: u8 = 1 << 3; -const TD_DD: u8 = 1; - -pub struct Intel8254x { - base: usize, - receive_buffer: [Dma<[u8; 16384]>; 16], - receive_ring: Dma<[Rd; 16]>, - transmit_buffer: [Dma<[u8; 16384]>; 16], - transmit_ring: Dma<[Td; 16]> -} - -impl Scheme for Intel8254x { - fn open(&self, _path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result { - if uid == 0 { - Ok(flags) - } else { - Err(Error::new(EACCES)) - } - } - - fn dup(&self, id: usize, _buf: &[u8]) -> Result { - Ok(id) - } - - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - 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; - - let data = &self.receive_buffer[tail as usize][.. rd.length as usize]; - - let mut i = 0; - while i < buf.len() && i < data.len() { - buf[i] = data[i]; - i += 1; - } - - unsafe { self.write(RDT, tail) }; - - return Ok(i); - } - } - - if id & O_NONBLOCK == O_NONBLOCK { - Ok(0) - } else { - Err(Error::new(EWOULDBLOCK)) - } - } - - fn write(&self, _id: usize, buf: &[u8]) -> Result { - loop { - let head = unsafe { self.read(TDH) }; - let mut tail = unsafe { self.read(TDT) }; - let old_tail = tail; - - tail += 1; - if tail >= self.transmit_ring.len() as u32 { - tail = 0; - } - - if tail != head { - let td = unsafe { &mut * (self.transmit_ring.as_ptr().offset(old_tail as isize) as *mut Td) }; - - td.cso = 0; - td.command = TD_CMD_EOP | TD_CMD_IFCS | TD_CMD_RS; - td.status = 0; - td.css = 0; - td.special = 0; - - td.length = (cmp::min(buf.len(), 0x3FFF)) as u16; - - let mut data = unsafe { slice::from_raw_parts_mut(self.transmit_buffer[old_tail as usize].as_ptr() as *mut u8, td.length as usize) }; - - let mut i = 0; - while i < buf.len() && i < data.len() { - data[i] = buf[i]; - i += 1; - } - - unsafe { self.write(TDT, tail) }; - - while td.status == 0 { - unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } - } - - return Ok(i); - } - - unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } - } - } - - fn fevent(&self, _id: usize, _flags: usize) -> Result { - Ok(0) - } - - fn fsync(&self, _id: usize) -> Result { - Ok(0) - } - - fn close(&self, _id: usize) -> Result { - Ok(0) - } -} - -impl Intel8254x { - pub unsafe fn new(base: usize) -> Result { - let mut module = Intel8254x { - base: base, - receive_buffer: [Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?], - receive_ring: Dma::zeroed()?, - transmit_buffer: [Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?], - transmit_ring: Dma::zeroed()? - }; - - module.init(); - - Ok(module) - } - - pub unsafe fn irq(&self) -> bool { - let icr = self.read(ICR); - icr != 0 - } - - pub fn next_read(&self) -> usize { - 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 { &* (self.receive_ring.as_ptr().offset(tail as isize) as *const Rd) }; - if rd.status & RD_DD == RD_DD { - return rd.length as usize; - } - } - - 0 - } - - pub unsafe fn read(&self, register: u32) -> u32 { - ptr::read_volatile((self.base + register as usize) as *mut u32) - } - - pub unsafe fn write(&self, register: u32, data: u32) -> u32 { - ptr::write_volatile((self.base + register as usize) as *mut u32, data); - ptr::read_volatile((self.base + register as usize) as *mut u32) - } - - pub unsafe fn flag(&self, register: u32, flag: u32, value: bool) { - if value { - self.write(register, self.read(register) | flag); - } else { - self.write(register, self.read(register) & (0xFFFFFFFF - flag)); - } - } - - pub unsafe fn init(&mut self) { - // Enable auto negotiate, link, clear reset, do not Invert Loss-Of Signal - self.flag(CTRL, CTRL_ASDE | CTRL_SLU, true); - self.flag(CTRL, CTRL_LRST, false); - self.flag(CTRL, CTRL_PHY_RST, false); - self.flag(CTRL, CTRL_ILOS, false); - - // No flow control - self.write(FCAH, 0); - self.write(FCAL, 0); - self.write(FCT, 0); - self.write(FCTTV, 0); - - // Do not use VLANs - self.flag(CTRL, CTRL_VME, false); - - // TODO: Clear statistical counters - - let mac_low = self.read(RAL0); - let mac_high = self.read(RAH0); - let mac = [mac_low as u8, - (mac_low >> 8) as u8, - (mac_low >> 16) as u8, - (mac_low >> 24) as u8, - mac_high as u8, - (mac_high >> 8) as u8]; - print!("{}", format!(" - MAC: {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); - let _ = setcfg("mac", &format!("{:>02X}.{:>02X}.{:>02X}.{:>02X}.{:>02X}.{:>02X}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); - - // - // MTA => 0; - // - - // Receive Buffer - for i in 0..self.receive_ring.len() { - self.receive_ring[i].buffer = self.receive_buffer[i].physical() as u64; - } - - self.write(RDBAH, (self.receive_ring.physical() >> 32) as u32); - self.write(RDBAL, self.receive_ring.physical() as u32); - self.write(RDLEN, (self.receive_ring.len() * mem::size_of::()) as u32); - self.write(RDH, 0); - self.write(RDT, self.receive_ring.len() as u32 - 1); - - // Transmit Buffer - for i in 0..self.transmit_ring.len() { - self.transmit_ring[i].buffer = self.transmit_buffer[i].physical() as u64; - } - - self.write(TDBAH, (self.transmit_ring.physical() >> 32) as u32); - self.write(TDBAL, self.transmit_ring.physical() as u32); - self.write(TDLEN, (self.transmit_ring.len() * mem::size_of::()) as u32); - 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.flag(RCTL, RCTL_EN, true); - self.flag(RCTL, RCTL_UPE, true); - // self.flag(RCTL, RCTL_MPE, true); - self.flag(RCTL, RCTL_LPE, true); - self.flag(RCTL, RCTL_LBM, false); - // RCTL.RDMTS = Minimum threshold size ??? - // RCTL.MO = Multicast offset - self.flag(RCTL, RCTL_BAM, true); - self.flag(RCTL, RCTL_BSIZE1, true); - self.flag(RCTL, RCTL_BSIZE2, false); - self.flag(RCTL, RCTL_BSEX, true); - self.flag(RCTL, RCTL_SECRC, true); - - self.flag(TCTL, TCTL_EN, true); - self.flag(TCTL, TCTL_PSP, true); - // TCTL.CT = Collision threshold - // TCTL.COLD = Collision distance - // TIPG Packet Gap - // TODO ... - } -} diff --git a/drivers/e1000d/src/main.rs b/drivers/e1000d/src/main.rs deleted file mode 100644 index b690029..0000000 --- a/drivers/e1000d/src/main.rs +++ /dev/null @@ -1,136 +0,0 @@ -#![feature(asm)] - -extern crate dma; -extern crate event; -extern crate netutils; -extern crate syscall; - -use std::cell::RefCell; -use std::env; -use std::fs::File; -use std::io::{Read, Write, Result}; -use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::sync::Arc; - -use event::EventQueue; -use syscall::{Packet, Scheme, MAP_WRITE}; -use syscall::error::EWOULDBLOCK; - -pub mod device; - -fn main() { - let mut args = env::args().skip(1); - - let mut name = args.next().expect("e1000d: no name provided"); - name.push_str("_e1000"); - - let bar_str = args.next().expect("e1000d: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("e1000d: failed to parse address"); - - let irq_str = args.next().expect("e1000d: no irq provided"); - let irq = irq_str.parse::().expect("e1000d: failed to parse irq"); - - print!("{}", format!(" + E1000 {} on: {:X}, IRQ: {}\n", name, bar, irq)); - - // Daemonize - if unsafe { syscall::clone(0).unwrap() } == 0 { - let socket_fd = syscall::open(":network", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("e1000d: failed to create network scheme"); - let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd) })); - - let address = unsafe { syscall::physmap(bar, 128*1024, MAP_WRITE).expect("e1000d: failed to map address") }; - { - let device = Arc::new(unsafe { device::Intel8254x::new(address).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); - } - } - - let next_read = device_irq.next_read(); - if next_read > 0 { - return Ok(Some(next_read)); - } - } - Ok(None) - }).expect("e1000d: failed to catch events on IRQ file"); - - let socket_packet = socket.clone(); - event_queue.add(socket_fd, move |_count: usize| -> Result> { - loop { - let mut packet = Packet::default(); - if socket_packet.borrow_mut().read(&mut packet)? == 0 { - break; - } - - let a = packet.a; - device.handle(&mut packet); - if packet.a == (-EWOULDBLOCK) as usize { - packet.a = a; - todo.borrow_mut().push(packet); - } else { - socket_packet.borrow_mut().write(&mut packet)?; - } - } - - let next_read = device.next_read(); - if next_read > 0 { - return Ok(Some(next_read)); - } - - Ok(None) - }).expect("e1000d: failed to catch events on IRQ file"); - - for event_count in event_queue.trigger_all(0).expect("e1000d: failed to trigger events") { - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ, - d: event_count - }).expect("e1000d: failed to write event"); - } - - loop { - let event_count = event_queue.run().expect("e1000d: failed to handle events"); - - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ, - d: event_count - }).expect("e1000d: failed to write event"); - } - } - unsafe { let _ = syscall::physunmap(address); } - } -} diff --git a/drivers/pcid/Cargo.toml b/drivers/pcid/Cargo.toml deleted file mode 100644 index f09b85a..0000000 --- a/drivers/pcid/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "pcid" -version = "0.1.0" - -[dependencies] -redox_syscall = { path = "../../syscall/" } -rustc-serialize = "0.3" -toml = "0.2" diff --git a/drivers/pcid/src/config.rs b/drivers/pcid/src/config.rs deleted file mode 100644 index 3bc4dba..0000000 --- a/drivers/pcid/src/config.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[derive(Debug, Default, RustcDecodable)] -pub struct Config { - pub drivers: Vec -} - -#[derive(Debug, Default, RustcDecodable)] -pub struct DriverConfig { - pub name: Option, - pub class: Option, - pub subclass: Option, - pub vendor: Option, - pub device: Option, - pub command: Option> -} diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs deleted file mode 100644 index 8202d6f..0000000 --- a/drivers/pcid/src/main.rs +++ /dev/null @@ -1,160 +0,0 @@ -#![feature(asm)] - -extern crate rustc_serialize; -extern crate syscall; -extern crate toml; - -use std::env; -use std::fs::File; -use std::io::Read; -use std::process::Command; -use syscall::iopl; - -use config::Config; -use pci::{Pci, PciBar, PciClass}; - -mod config; -mod pci; - -fn main() { - let mut config = Config::default(); - - let mut args = env::args().skip(1); - if let Some(config_path) = args.next() { - if let Ok(mut config_file) = File::open(&config_path) { - let mut config_data = String::new(); - if let Ok(_) = config_file.read_to_string(&mut config_data) { - config = toml::decode_str(&config_data).unwrap_or(Config::default()); - } - } - } - - unsafe { iopl(3).unwrap() }; - - print!("PCI BS/DV/FN VEND:DEVI CL.SC.IN.RV\n"); - - let pci = Pci::new(); - for bus in pci.buses() { - for dev in bus.devs() { - for func in dev.funcs() { - if let Some(header) = func.header() { - let pci_class = PciClass::from(header.class); - - let mut string = format!("PCI {:>02X}/{:>02X}/{:>02X} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", - bus.num, dev.num, func.num, - header.vendor_id, header.device_id, - header.class, header.subclass, header.interface, header.revision, - pci_class); - - match pci_class { - PciClass::Storage => match header.subclass { - 0x01 => { - string.push_str(" IDE"); - }, - 0x06 => { - string.push_str(" SATA"); - }, - _ => () - }, - PciClass::SerialBus => match header.subclass { - 0x03 => match header.interface { - 0x00 => { - string.push_str(" UHCI"); - }, - 0x10 => { - string.push_str(" OHCI"); - }, - 0x20 => { - string.push_str(" EHCI"); - }, - 0x30 => { - string.push_str(" XHCI"); - }, - _ => () - }, - _ => () - }, - _ => () - } - - for i in 0..header.bars.len() { - match PciBar::from(header.bars[i]) { - PciBar::None => (), - PciBar::Memory(address) => string.push_str(&format!(" {}={:>08X}", i, address)), - PciBar::Port(address) => string.push_str(&format!(" {}={:>04X}", i, address)) - } - } - - string.push('\n'); - - print!("{}", string); - - for driver in config.drivers.iter() { - if let Some(class) = driver.class { - if class != header.class { continue; } - } - - if let Some(subclass) = driver.subclass { - if subclass != header.subclass { continue; } - } - - if let Some(vendor) = driver.vendor { - if vendor != header.vendor_id { continue; } - } - - if let Some(device) = driver.device { - if device != header.device_id { continue; } - } - - if let Some(ref args) = driver.command { - // Enable bus mastering - unsafe { - let cmd = pci.read(bus.num, dev.num, func.num, 0x04); - pci.write(bus.num, dev.num, func.num, 0x04, cmd | 4); - } - - let mut args = args.iter(); - if let Some(program) = args.next() { - let mut command = Command::new(program); - for arg in args { - let bar_arg = |i| -> String { - match PciBar::from(header.bars[i]) { - PciBar::None => String::new(), - PciBar::Memory(address) => format!("{:>08X}", address), - PciBar::Port(address) => format!("{:>04X}", address) - } - }; - let arg = match arg.as_str() { - "$BUS" => format!("{:>02X}", bus.num), - "$DEV" => format!("{:>02X}", dev.num), - "$FUNC" => format!("{:>02X}", func.num), - "$NAME" => format!("pci-{:>02X}.{:>02X}.{:>02X}", bus.num, dev.num, func.num), - "$BAR0" => bar_arg(0), - "$BAR1" => bar_arg(1), - "$BAR2" => bar_arg(2), - "$BAR3" => bar_arg(3), - "$BAR4" => bar_arg(4), - "$BAR5" => bar_arg(5), - "$IRQ" => format!("{}", header.interrupt_line), - _ => arg.clone() - }; - command.arg(&arg); - } - - println!("PCID SPAWN {:?}", command); - - match command.spawn() { - Ok(mut child) => match child.wait() { - Ok(_status) => (), //println!("pcid: waited for {}: {:?}", line, status.code()), - Err(err) => println!("pcid: failed to wait for {:?}: {}", command, err) - }, - Err(err) => println!("pcid: failed to execute {:?}: {}", command, err) - } - } - } - } - } - } - } - } -} diff --git a/drivers/pcid/src/pci/bar.rs b/drivers/pcid/src/pci/bar.rs deleted file mode 100644 index 190fa05..0000000 --- a/drivers/pcid/src/pci/bar.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[derive(Debug)] -pub enum PciBar { - None, - Memory(u32), - Port(u16) -} - -impl From for PciBar { - fn from(bar: u32) -> Self { - if bar & 0xFFFFFFFC == 0 { - PciBar::None - } else if bar & 1 == 0 { - PciBar::Memory(bar & 0xFFFFFFF0) - } else { - PciBar::Port((bar & 0xFFFC) as u16) - } - } -} diff --git a/drivers/pcid/src/pci/bus.rs b/drivers/pcid/src/pci/bus.rs deleted file mode 100644 index 120fa45..0000000 --- a/drivers/pcid/src/pci/bus.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::{Pci, PciDev}; - -pub struct PciBus<'pci> { - pub pci: &'pci Pci, - pub num: u8 -} - -impl<'pci> PciBus<'pci> { - pub fn devs(&'pci self) -> PciBusIter<'pci> { - PciBusIter::new(self) - } - - pub unsafe fn read(&self, dev: u8, func: u8, offset: u8) -> u32 { - self.pci.read(self.num, dev, func, offset) - } -} - -pub struct PciBusIter<'pci> { - bus: &'pci PciBus<'pci>, - num: u32 -} - -impl<'pci> PciBusIter<'pci> { - pub fn new(bus: &'pci PciBus<'pci>) -> Self { - PciBusIter { - bus: bus, - num: 0 - } - } -} - -impl<'pci> Iterator for PciBusIter<'pci> { - type Item = PciDev<'pci>; - fn next(&mut self) -> Option { - if self.num < 32 { - let dev = PciDev { - bus: self.bus, - num: self.num as u8 - }; - self.num += 1; - Some(dev) - } else { - None - } - } -} diff --git a/drivers/pcid/src/pci/class.rs b/drivers/pcid/src/pci/class.rs deleted file mode 100644 index 21f7f69..0000000 --- a/drivers/pcid/src/pci/class.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[derive(Debug)] -pub enum PciClass { - Legacy, - Storage, - Network, - Display, - Multimedia, - Memory, - Bridge, - SimpleComms, - Peripheral, - Input, - Docking, - Processor, - SerialBus, - Wireless, - IntelligentIo, - SatelliteComms, - Cryptography, - SignalProc, - Reserved(u8), - Unknown -} - -impl From for PciClass { - fn from(class: u8) -> PciClass { - match class { - 0x00 => PciClass::Legacy, - 0x01 => PciClass::Storage, - 0x02 => PciClass::Network, - 0x03 => PciClass::Display, - 0x04 => PciClass::Multimedia, - 0x05 => PciClass::Memory, - 0x06 => PciClass::Bridge, - 0x07 => PciClass::SimpleComms, - 0x08 => PciClass::Peripheral, - 0x09 => PciClass::Input, - 0x0A => PciClass::Docking, - 0x0B => PciClass::Processor, - 0x0C => PciClass::SerialBus, - 0x0D => PciClass::Wireless, - 0x0E => PciClass::IntelligentIo, - 0x0F => PciClass::SatelliteComms, - 0x10 => PciClass::Cryptography, - 0x11 => PciClass::SignalProc, - 0xFF => PciClass::Unknown, - reserved => PciClass::Reserved(reserved) - } - } -} diff --git a/drivers/pcid/src/pci/dev.rs b/drivers/pcid/src/pci/dev.rs deleted file mode 100644 index 0508888..0000000 --- a/drivers/pcid/src/pci/dev.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::{PciBus, PciFunc}; - -pub struct PciDev<'pci> { - pub bus: &'pci PciBus<'pci>, - pub num: u8 -} - -impl<'pci> PciDev<'pci> { - pub fn funcs(&'pci self) -> PciDevIter<'pci> { - PciDevIter::new(self) - } - - pub unsafe fn read(&self, func: u8, offset: u8) -> u32 { - self.bus.read(self.num, func, offset) - } -} - -pub struct PciDevIter<'pci> { - dev: &'pci PciDev<'pci>, - num: u32 -} - -impl<'pci> PciDevIter<'pci> { - pub fn new(dev: &'pci PciDev<'pci>) -> Self { - PciDevIter { - dev: dev, - num: 0 - } - } -} - -impl<'pci> Iterator for PciDevIter<'pci> { - type Item = PciFunc<'pci>; - fn next(&mut self) -> Option { - if self.num < 8 { - let func = PciFunc { - dev: self.dev, - num: self.num as u8 - }; - self.num += 1; - Some(func) - } else { - None - } - } -} diff --git a/drivers/pcid/src/pci/func.rs b/drivers/pcid/src/pci/func.rs deleted file mode 100644 index 578e5c6..0000000 --- a/drivers/pcid/src/pci/func.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::ops::DerefMut; - -use super::{PciDev, PciHeader}; - -pub struct PciFunc<'pci> { - pub dev: &'pci PciDev<'pci>, - pub num: u8 -} - -impl<'pci> PciFunc<'pci> { - pub fn header(&self) -> Option { - if unsafe { self.read(0) } != 0xFFFFFFFF { - let mut header = PciHeader::default(); - { - let dwords = header.deref_mut(); - dwords.iter_mut().fold(0usize, |offset, dword| { - *dword = unsafe { self.read(offset as u8) }; - offset + 4 - }); - } - Some(header) - } else { - None - } - } - - pub unsafe fn read(&self, offset: u8) -> u32 { - self.dev.read(self.num, offset) - } -} diff --git a/drivers/pcid/src/pci/header.rs b/drivers/pcid/src/pci/header.rs deleted file mode 100644 index 2cc335e..0000000 --- a/drivers/pcid/src/pci/header.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::ops::{Deref, DerefMut}; -use std::{slice, mem}; - -#[derive(Debug, Default)] -#[repr(packed)] -pub struct PciHeader { - pub vendor_id: u16, - pub device_id: u16, - pub command: u16, - pub status: u16, - pub revision: u8, - pub interface: u8, - pub subclass: u8, - pub class: u8, - pub cache_line_size: u8, - pub latency_timer: u8, - pub header_type: u8, - pub bist: u8, - pub bars: [u32; 6], - pub cardbus_cis_ptr: u32, - pub subsystem_vendor_id: u16, - pub subsystem_id: u16, - pub expansion_rom_bar: u32, - pub capabilities: u8, - pub reserved: [u8; 7], - pub interrupt_line: u8, - pub interrupt_pin: u8, - pub min_grant: u8, - pub max_latency: u8 -} - -impl Deref for PciHeader { - type Target = [u32]; - fn deref(&self) -> &[u32] { - unsafe { slice::from_raw_parts(self as *const PciHeader as *const u32, mem::size_of::()/4) as &[u32] } - } -} - -impl DerefMut for PciHeader { - fn deref_mut(&mut self) -> &mut [u32] { - unsafe { slice::from_raw_parts_mut(self as *mut PciHeader as *mut u32, mem::size_of::()/4) as &mut [u32] } - } -} diff --git a/drivers/pcid/src/pci/mod.rs b/drivers/pcid/src/pci/mod.rs deleted file mode 100644 index d2a8e89..0000000 --- a/drivers/pcid/src/pci/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -pub use self::bar::PciBar; -pub use self::bus::{PciBus, PciBusIter}; -pub use self::class::PciClass; -pub use self::dev::{PciDev, PciDevIter}; -pub use self::func::PciFunc; -pub use self::header::PciHeader; - -mod bar; -mod bus; -mod class; -mod dev; -mod func; -mod header; - -pub struct Pci; - -impl Pci { - pub fn new() -> Self { - Pci - } - - pub fn buses<'pci>(&'pci self) -> PciIter<'pci> { - PciIter::new(self) - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub unsafe fn read(&self, bus: u8, dev: u8, func: u8, offset: u8) -> u32 { - let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); - let value: u32; - asm!("mov dx, 0xCF8 - out dx, eax - mov dx, 0xCFC - in eax, dx" - : "={eax}"(value) : "{eax}"(address) : "dx" : "intel", "volatile"); - value - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub unsafe fn write(&self, bus: u8, dev: u8, func: u8, offset: u8, value: u32) { - let address = 0x80000000 | ((bus as u32) << 16) | ((dev as u32) << 11) | ((func as u32) << 8) | ((offset as u32) & 0xFC); - asm!("mov dx, 0xCF8 - out dx, eax" - : : "{eax}"(address) : "dx" : "intel", "volatile"); - asm!("mov dx, 0xCFC - out dx, eax" - : : "{eax}"(value) : "dx" : "intel", "volatile"); - } -} - -pub struct PciIter<'pci> { - pci: &'pci Pci, - num: u32 -} - -impl<'pci> PciIter<'pci> { - pub fn new(pci: &'pci Pci) -> Self { - PciIter { - pci: pci, - num: 0 - } - } -} - -impl<'pci> Iterator for PciIter<'pci> { - type Item = PciBus<'pci>; - fn next(&mut self) -> Option { - if self.num < 256 { - let bus = PciBus { - pci: self.pci, - num: self.num as u8 - }; - self.num += 1; - Some(bus) - } else { - None - } - } -} diff --git a/drivers/ps2d/Cargo.toml b/drivers/ps2d/Cargo.toml deleted file mode 100644 index 22863b9..0000000 --- a/drivers/ps2d/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "ps2d" -version = "0.1.0" - -[dependencies] -bitflags = "*" -event = { path = "../../crates/event/" } -io = { path = "../../crates/io/" } -orbclient = "0.2" -redox_syscall = { path = "../../syscall/" } diff --git a/drivers/ps2d/src/controller.rs b/drivers/ps2d/src/controller.rs deleted file mode 100644 index e68d4f1..0000000 --- a/drivers/ps2d/src/controller.rs +++ /dev/null @@ -1,233 +0,0 @@ -use io::{Io, Pio, ReadOnly, WriteOnly}; - -bitflags! { - flags StatusFlags: u8 { - const OUTPUT_FULL = 1, - const INPUT_FULL = 1 << 1, - const SYSTEM = 1 << 2, - const COMMAND = 1 << 3, - // Chipset specific - const KEYBOARD_LOCK = 1 << 4, - // Chipset specific - const SECOND_OUTPUT_FULL = 1 << 5, - const TIME_OUT = 1 << 6, - const PARITY = 1 << 7 - } -} - -bitflags! { - flags ConfigFlags: u8 { - const FIRST_INTERRUPT = 1, - const SECOND_INTERRUPT = 1 << 1, - const POST_PASSED = 1 << 2, - // 1 << 3 should be zero - const CONFIG_RESERVED_3 = 1 << 3, - const FIRST_DISABLED = 1 << 4, - const SECOND_DISABLED = 1 << 5, - const FIRST_TRANSLATE = 1 << 6, - // 1 << 7 should be zero - const CONFIG_RESERVED_7 = 1 << 7, - } -} - -#[repr(u8)] -#[allow(dead_code)] -enum Command { - ReadConfig = 0x20, - WriteConfig = 0x60, - DisableSecond = 0xA7, - EnableSecond = 0xA8, - TestSecond = 0xA9, - TestController = 0xAA, - TestFirst = 0xAB, - Diagnostic = 0xAC, - DisableFirst = 0xAD, - EnableFirst = 0xAE, - WriteSecond = 0xD4 -} - -#[repr(u8)] -#[allow(dead_code)] -enum KeyboardCommand { - EnableReporting = 0xF4, - SetDefaults = 0xF6, - Reset = 0xFF -} - -#[repr(u8)] -enum KeyboardCommandData { - ScancodeSet = 0xF0 -} - -#[repr(u8)] -#[allow(dead_code)] -enum MouseCommand { - GetDeviceId = 0xF2, - EnableReporting = 0xF4, - SetDefaults = 0xF6, - Reset = 0xFF -} - -#[repr(u8)] -enum MouseCommandData { - SetSampleRate = 0xF3, -} - -pub struct Ps2 { - data: Pio, - status: ReadOnly>, - command: WriteOnly> -} - -impl Ps2 { - pub fn new() -> Self { - Ps2 { - data: Pio::new(0x60), - status: ReadOnly::new(Pio::new(0x64)), - command: WriteOnly::new(Pio::new(0x64)), - } - } - - fn status(&mut self) -> StatusFlags { - StatusFlags::from_bits_truncate(self.status.read()) - } - - fn wait_write(&mut self) { - while self.status().contains(INPUT_FULL) {} - } - - fn wait_read(&mut self) { - while ! self.status().contains(OUTPUT_FULL) {} - } - - fn flush_read(&mut self) { - while self.status().contains(OUTPUT_FULL) { - print!("FLUSH: {:X}\n", self.data.read()); - } - } - - fn command(&mut self, command: Command) { - self.wait_write(); - self.command.write(command as u8); - } - - fn read(&mut self) -> u8 { - self.wait_read(); - self.data.read() - } - - fn write(&mut self, data: u8) { - self.wait_write(); - self.data.write(data); - } - - fn config(&mut self) -> ConfigFlags { - self.command(Command::ReadConfig); - ConfigFlags::from_bits_truncate(self.read()) - } - - fn set_config(&mut self, config: ConfigFlags) { - self.command(Command::WriteConfig); - self.write(config.bits()); - } - - fn keyboard_command(&mut self, command: KeyboardCommand) -> u8 { - self.write(command as u8); - self.read() - } - - fn keyboard_command_data(&mut self, command: KeyboardCommandData, data: u8) -> u8 { - self.write(command as u8); - assert_eq!(self.read(), 0xFA); - self.write(data as u8); - self.read() - } - - fn mouse_command(&mut self, command: MouseCommand) -> u8 { - self.command(Command::WriteSecond); - self.write(command as u8); - self.read() - } - - fn mouse_command_data(&mut self, command: MouseCommandData, data: u8) -> u8 { - self.command(Command::WriteSecond); - self.write(command as u8); - assert_eq!(self.read(), 0xFA); - self.command(Command::WriteSecond); - self.write(data as u8); - self.read() - } - - pub fn init(&mut self) -> bool { - // Disable devices - self.command(Command::DisableFirst); - self.command(Command::DisableSecond); - - // Clear remaining data - self.flush_read(); - - // Disable clocks, disable interrupts, and disable translate - { - let mut config = self.config(); - config.insert(FIRST_DISABLED); - config.insert(SECOND_DISABLED); - config.remove(FIRST_TRANSLATE); - config.remove(FIRST_INTERRUPT); - config.remove(SECOND_INTERRUPT); - self.set_config(config); - } - - // Perform the self test - self.command(Command::TestController); - assert_eq!(self.read(), 0x55); - - // Enable devices - self.command(Command::EnableFirst); - self.command(Command::EnableSecond); - - // Reset keyboard - assert_eq!(self.keyboard_command(KeyboardCommand::Reset), 0xFA); - assert_eq!(self.read(), 0xAA); - self.flush_read(); - - // Set scancode set to 2 - assert_eq!(self.keyboard_command_data(KeyboardCommandData::ScancodeSet, 2), 0xFA); - - // Reset mouse and set up scroll - // TODO: Check for ack - assert_eq!(self.mouse_command(MouseCommand::Reset), 0xFA); - assert_eq!(self.read(), 0xAA); - assert_eq!(self.read(), 0x00); - self.flush_read(); - - // Enable extra packet on mouse - assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA); - assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 100), 0xFA); - assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 80), 0xFA); - assert_eq!(self.mouse_command(MouseCommand::GetDeviceId), 0xFA); - let mouse_id = self.read(); - let mouse_extra = mouse_id == 3; - - // Set sample rate to maximum - assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA); - - // Enable data reporting - assert_eq!(self.keyboard_command(KeyboardCommand::EnableReporting), 0xFA); - assert_eq!(self.mouse_command(MouseCommand::EnableReporting), 0xFA); - - // Enable clocks and interrupts - { - let mut config = self.config(); - config.remove(FIRST_DISABLED); - config.remove(SECOND_DISABLED); - config.insert(FIRST_TRANSLATE); - config.insert(FIRST_INTERRUPT); - config.insert(SECOND_INTERRUPT); - self.set_config(config); - } - - self.flush_read(); - - mouse_extra - } -} diff --git a/drivers/ps2d/src/keymap.rs b/drivers/ps2d/src/keymap.rs deleted file mode 100644 index 5697e52..0000000 --- a/drivers/ps2d/src/keymap.rs +++ /dev/null @@ -1,148 +0,0 @@ -pub mod english { - static ENGLISH: [[char; 2]; 58] = [ - ['\0', '\0'], - ['\x1B', '\x1B'], - ['1', '!'], - ['2', '@'], - ['3', '#'], - ['4', '$'], - ['5', '%'], - ['6', '^'], - ['7', '&'], - ['8', '*'], - ['9', '('], - ['0', ')'], - ['-', '_'], - ['=', '+'], - ['\x7F', '\x7F'], - ['\t', '\t'], - ['q', 'Q'], - ['w', 'W'], - ['e', 'E'], - ['r', 'R'], - ['t', 'T'], - ['y', 'Y'], - ['u', 'U'], - ['i', 'I'], - ['o', 'O'], - ['p', 'P'], - ['[', '{'], - [']', '}'], - ['\n', '\n'], - ['\0', '\0'], - ['a', 'A'], - ['s', 'S'], - ['d', 'D'], - ['f', 'F'], - ['g', 'G'], - ['h', 'H'], - ['j', 'J'], - ['k', 'K'], - ['l', 'L'], - [';', ':'], - ['\'', '"'], - ['`', '~'], - ['\0', '\0'], - ['\\', '|'], - ['z', 'Z'], - ['x', 'X'], - ['c', 'C'], - ['v', 'V'], - ['b', 'B'], - ['n', 'N'], - ['m', 'M'], - [',', '<'], - ['.', '>'], - ['/', '?'], - ['\0', '\0'], - ['\0', '\0'], - ['\0', '\0'], - [' ', ' '] - ]; - - pub fn get_char(scancode: u8, shift: bool) -> char { - if let Some(c) = ENGLISH.get(scancode as usize) { - if shift { - c[1] - } else { - c[0] - } - } else { - '\0' - } - } -} -pub mod dvorak { - static DVORAK: [[char; 2]; 58] = [ - ['\0', '\0'], - ['\x1B', '\x1B'], - ['1', '!'], - ['2', '@'], - ['3', '#'], - ['4', '$'], - ['5', '%'], - ['6', '^'], - ['7', '&'], - ['8', '*'], - ['9', '('], - ['0', ')'], - ['[', '{'], - [']', '}'], - ['\x7F', '\x7F'], - ['\t', '\t'], - ['\'', '"'], - [',', '<'], - ['.', '>'], - ['p', 'P'], - ['y', 'Y'], - ['f', 'F'], - ['g', 'G'], - ['c', 'C'], - ['r', 'R'], - ['l', 'L'], - ['/', '?'], - ['=', '+'], - ['\n', '\n'], - ['\0', '\0'], - ['a', 'A'], - ['o', 'O'], - ['e', 'E'], - ['u', 'U'], - ['i', 'I'], - ['d', 'D'], - ['h', 'H'], - ['t', 'T'], - ['n', 'N'], - ['s', 'S'], - ['-', '_'], - ['`', '~'], - ['\0', '\0'], - ['\\', '|'], - [';', ':'], - ['q', 'Q'], - ['j', 'J'], - ['k', 'K'], - ['x', 'X'], - ['b', 'B'], - ['m', 'M'], - ['w', 'W'], - ['v', 'V'], - ['z', 'Z'], - ['\0', '\0'], - ['\0', '\0'], - ['\0', '\0'], - [' ', ' '] - ]; - - pub fn get_char(scancode: u8, shift: bool) -> char { - if let Some(c) = DVORAK.get(scancode as usize) { - if shift { - c[1] - } else { - c[0] - } - } else { - '\0' - } - } -} diff --git a/drivers/ps2d/src/main.rs b/drivers/ps2d/src/main.rs deleted file mode 100644 index 7c67bd3..0000000 --- a/drivers/ps2d/src/main.rs +++ /dev/null @@ -1,193 +0,0 @@ -#![feature(asm)] - -#[macro_use] -extern crate bitflags; -extern crate event; -extern crate io; -extern crate orbclient; -extern crate syscall; - -use std::env; -use std::fs::File; -use std::io::{Read, Write, Result}; -use std::os::unix::io::AsRawFd; -use std::mem; - -use event::EventQueue; -use orbclient::{KeyEvent, MouseEvent}; -use syscall::iopl; - -mod controller; -mod keymap; - -bitflags! { - flags MousePacketFlags: u8 { - const LEFT_BUTTON = 1, - const RIGHT_BUTTON = 1 << 1, - const MIDDLE_BUTTON = 1 << 2, - const ALWAYS_ON = 1 << 3, - const X_SIGN = 1 << 4, - const Y_SIGN = 1 << 5, - const X_OVERFLOW = 1 << 6, - const Y_OVERFLOW = 1 << 7 - } -} - -struct Ps2d<'a> { - input: File, - lshift: bool, - rshift: bool, - packets: [u8; 4], - packet_i: usize, - extra_packet: bool, - //Keymap function - get_char: &'a Fn(u8,bool) -> char -} - -impl<'a> Ps2d<'a> { - fn new(input: File, extra_packet: bool, keymap: &'a Fn(u8,bool) -> char) -> Self { - Ps2d { - input: input, - lshift: false, - rshift: false, - packets: [0; 4], - packet_i: 0, - extra_packet: extra_packet, - get_char: keymap - } - } - - fn handle(&mut self, keyboard: bool, data: u8) { - if keyboard { - let (scancode, pressed) = if data >= 0x80 { - (data - 0x80, false) - } else { - (data, true) - }; - - if scancode == 0x2A { - self.lshift = pressed; - } else if scancode == 0x36 { - self.rshift = pressed; - } - - self.input.write(&KeyEvent { - character: (self.get_char)(scancode, self.lshift || self.rshift), - scancode: scancode, - pressed: pressed - }.to_event()).expect("ps2d: failed to write key event"); - } else { - self.packets[self.packet_i] = data; - self.packet_i += 1; - - let flags = MousePacketFlags::from_bits_truncate(self.packets[0]); - if ! flags.contains(ALWAYS_ON) { - println!("MOUSE MISALIGN {:X}", self.packets[0]); - - self.packets = [0; 4]; - self.packet_i = 0; - } else if self.packet_i >= self.packets.len() || (!self.extra_packet && self.packet_i >= 3) { - if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) { - let mut dx = self.packets[1] as i32; - if flags.contains(X_SIGN) { - dx -= 0x100; - } - - let mut dy = -(self.packets[2] as i32); - if flags.contains(Y_SIGN) { - dy += 0x100; - } - - let _extra = if self.extra_packet { - self.packets[3] - } else { - 0 - }; - - self.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}", self.packets[0], self.packets[1], self.packets[2], self.packets[3]); - } - - self.packets = [0; 4]; - self.packet_i = 0; - } - } - } -} - -fn main() { - // Daemonize - if unsafe { syscall::clone(0).unwrap() } == 0 { - unsafe { - iopl(3).expect("ps2d: failed to get I/O permission"); - asm!("cli" : : : : "intel", "volatile"); - } - - let input = File::open("display:input").expect("ps2d: failed to open display:input"); - - let extra_packet = controller::Ps2::new().init(); - let keymap = match env::args().skip(1).next() { - Some(k) => match k.to_lowercase().as_ref() { - "dvorak" => (keymap::dvorak::get_char), - "english" => (keymap::english::get_char), - &_ => (keymap::english::get_char) - }, - None => (keymap::english::get_char) - }; - let mut ps2d = Ps2d::new(input, extra_packet,&keymap); - - let mut event_queue = EventQueue::<(bool, u8)>::new().expect("ps2d: failed to create event queue"); - - let mut key_irq = File::open("irq:1").expect("ps2d: failed to open irq:1"); - - event_queue.add(key_irq.as_raw_fd(), move |_count: usize| -> Result> { - let mut irq = [0; 8]; - if key_irq.read(&mut irq)? >= mem::size_of::() { - let data: u8; - unsafe { - asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); - } - - key_irq.write(&irq)?; - - Ok(Some((true, data))) - } else { - Ok(None) - } - }).expect("ps2d: failed to poll irq:1"); - - let mut mouse_irq = File::open("irq:12").expect("ps2d: failed to open irq:12"); - - event_queue.add(mouse_irq.as_raw_fd(), move |_count: usize| -> Result> { - let mut irq = [0; 8]; - if mouse_irq.read(&mut irq)? >= mem::size_of::() { - let data: u8; - unsafe { - asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); - } - - mouse_irq.write(&irq)?; - - Ok(Some((false, data))) - } else { - Ok(None) - } - }).expect("ps2d: failed to poll irq:12"); - - for (keyboard, data) in event_queue.trigger_all(0).expect("ps2d: failed to trigger events") { - ps2d.handle(keyboard, data); - } - - loop { - let (keyboard, data) = event_queue.run().expect("ps2d: failed to handle events"); - ps2d.handle(keyboard, data); - } - } -} diff --git a/drivers/rtl8168d/Cargo.toml b/drivers/rtl8168d/Cargo.toml deleted file mode 100644 index e72a130..0000000 --- a/drivers/rtl8168d/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "rtl8168d" -version = "0.1.0" - -[dependencies] -bitflags = "*" -dma = { path = "../../crates/dma/" } -event = { path = "../../crates/event/" } -io = { path = "../../crates/io/" } -netutils = { path = "../../programs/netutils/" } -redox_syscall = { path = "../../syscall/" } diff --git a/drivers/rtl8168d/src/device.rs b/drivers/rtl8168d/src/device.rs deleted file mode 100644 index 7ed7d59..0000000 --- a/drivers/rtl8168d/src/device.rs +++ /dev/null @@ -1,300 +0,0 @@ -use std::mem; - -use dma::Dma; -use io::{Mmio, Io, ReadOnly}; -use netutils::setcfg; -use syscall::error::{Error, EACCES, EWOULDBLOCK, Result}; -use syscall::flag::O_NONBLOCK; -use syscall::scheme::SchemeMut; - -#[repr(packed)] -struct Regs { - mac: [Mmio; 2], - _mar: [Mmio; 2], - _dtccr: [Mmio; 2], - _rsv0: [Mmio; 2], - tnpds: [Mmio; 2], - thpds: [Mmio; 2], - _rsv1: [Mmio; 7], - cmd: Mmio, - tppoll: Mmio, - _rsv2: [Mmio; 3], - imr: Mmio, - isr: Mmio, - tcr: Mmio, - rcr: Mmio, - _tctr: Mmio, - _rsv3: Mmio, - cmd_9346: Mmio, - _config: [Mmio; 6], - _rsv4: Mmio, - timer_int: Mmio, - _rsv5: Mmio, - _phys_ar: Mmio, - _rsv6: [Mmio; 2], - phys_sts: ReadOnly>, - _rsv7: [Mmio; 23], - _wakeup: [Mmio; 16], - _crc: [Mmio; 5], - _rsv8: [Mmio; 12], - rms: Mmio, - _rsv9: Mmio, - _c_plus_cr: Mmio, - _rsv10: Mmio, - rdsar: [Mmio; 2], - mtps: Mmio, - _rsv11: [Mmio; 19], -} - -const OWN: u32 = 1 << 31; -const EOR: u32 = 1 << 30; -const FS: u32 = 1 << 29; -const LS: u32 = 1 << 28; - -#[repr(packed)] -struct Rd { - ctrl: Mmio, - _vlan: Mmio, - buffer: Mmio -} - -#[repr(packed)] -struct Td { - ctrl: Mmio, - _vlan: Mmio, - buffer: Mmio -} - -pub struct Rtl8168 { - regs: &'static mut Regs, - receive_buffer: [Dma<[Mmio; 0x1FF8]>; 16], - receive_ring: Dma<[Rd; 16]>, - transmit_buffer: [Dma<[Mmio; 7552]>; 16], - transmit_ring: Dma<[Td; 16]>, - transmit_buffer_h: [Dma<[Mmio; 7552]>; 1], - transmit_ring_h: Dma<[Td; 1]> -} - -impl SchemeMut for Rtl8168 { - fn open(&mut self, _path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result { - if uid == 0 { - Ok(flags) - } else { - Err(Error::new(EACCES)) - } - } - - fn dup(&mut self, id: usize, _buf: &[u8]) -> Result { - Ok(id) - } - - fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { - for (rd_i, rd) in self.receive_ring.iter_mut().enumerate() { - if ! rd.ctrl.readf(OWN) { - let rd_len = rd.ctrl.read() & 0x3FFF; - - let data = &self.receive_buffer[rd_i as usize]; - - let mut i = 0; - while i < buf.len() && i < rd_len as usize { - buf[i] = data[i].read(); - i += 1; - } - - let eor = rd.ctrl.read() & EOR; - rd.ctrl.write(OWN | eor | data.len() as u32); - - return Ok(i); - } - } - - if id & O_NONBLOCK == O_NONBLOCK { - Ok(0) - } else { - Err(Error::new(EWOULDBLOCK)) - } - } - - fn write(&mut self, _id: usize, buf: &[u8]) -> Result { - loop { - for (td_i, td) in self.transmit_ring.iter_mut().enumerate() { - if ! td.ctrl.readf(OWN) { - - let mut data = &mut self.transmit_buffer[td_i as usize]; - - let mut i = 0; - while i < buf.len() && i < data.len() { - data[i].write(buf[i]); - i += 1; - } - - let eor = td.ctrl.read() & EOR; - td.ctrl.write(OWN | eor | FS | LS | i as u32); - - self.regs.tppoll.writef(1 << 6, true); //Notify of normal priority packet - - while self.regs.tppoll.readf(1 << 6) { - unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } - } - - return Ok(i); - } - } - - unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); } - } - } - - fn fevent(&mut self, _id: usize, _flags: usize) -> Result { - Ok(0) - } - - fn fsync(&mut self, _id: usize) -> Result { - Ok(0) - } - - fn close(&mut self, _id: usize) -> Result { - Ok(0) - } -} - -impl Rtl8168 { - pub unsafe fn new(base: usize) -> Result { - assert_eq!(mem::size_of::(), 256); - - let regs = &mut *(base as *mut Regs); - assert_eq!(®s.tnpds as *const _ as usize - base, 0x20); - assert_eq!(®s.cmd as *const _ as usize - base, 0x37); - assert_eq!(®s.tcr as *const _ as usize - base, 0x40); - assert_eq!(®s.rcr as *const _ as usize - base, 0x44); - assert_eq!(®s.cmd_9346 as *const _ as usize - base, 0x50); - assert_eq!(®s.phys_sts as *const _ as usize - base, 0x6C); - assert_eq!(®s.rms as *const _ as usize - base, 0xDA); - assert_eq!(®s.rdsar as *const _ as usize - base, 0xE4); - assert_eq!(®s.mtps as *const _ as usize - base, 0xEC); - - let mut module = Rtl8168 { - regs: regs, - receive_buffer: [Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?], - receive_ring: Dma::zeroed()?, - transmit_buffer: [Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, - Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?], - transmit_ring: Dma::zeroed()?, - transmit_buffer_h: [Dma::zeroed()?], - transmit_ring_h: Dma::zeroed()? - }; - - module.init(); - - Ok(module) - } - - pub unsafe fn irq(&mut self) -> u16 { - // Read and then clear the ISR - let isr = self.regs.isr.read(); - self.regs.isr.write(isr); - let imr = self.regs.imr.read(); - isr & imr - } - - pub fn next_read(&self) -> usize { - for rd in self.receive_ring.iter() { - if ! rd.ctrl.readf(OWN) { - return rd.ctrl.read() as usize & 0x3FFF; - } - } - 0 - } - - pub unsafe fn init(&mut self) { - let mac_low = self.regs.mac[0].read(); - let mac_high = self.regs.mac[1].read(); - let mac = [mac_low as u8, - (mac_low >> 8) as u8, - (mac_low >> 16) as u8, - (mac_low >> 24) as u8, - mac_high as u8, - (mac_high >> 8) as u8]; - print!("{}", format!(" - MAC: {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); - let _ = setcfg("mac", &format!("{:>02X}.{:>02X}.{:>02X}.{:>02X}.{:>02X}.{:>02X}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); - - // Reset - this will disable tx and rx, reinitialize FIFOs, and set the system buffer pointer to the initial value - self.regs.cmd.writef(1 << 4, true); - while self.regs.cmd.readf(1 << 4) {} - - // Set up rx buffers - for i in 0..self.receive_ring.len() { - let rd = &mut self.receive_ring[i]; - let data = &mut self.receive_buffer[i]; - rd.buffer.write(data.physical() as u64); - rd.ctrl.write(OWN | data.len() as u32); - } - if let Some(mut rd) = self.receive_ring.last_mut() { - rd.ctrl.writef(EOR, true); - } - - // Set up normal priority tx buffers - for i in 0..self.transmit_ring.len() { - self.transmit_ring[i].buffer.write(self.transmit_buffer[i].physical() as u64); - } - if let Some(mut td) = self.transmit_ring.last_mut() { - td.ctrl.writef(EOR, true); - } - - // Set up high priority tx buffers - for i in 0..self.transmit_ring_h.len() { - self.transmit_ring_h[i].buffer.write(self.transmit_buffer_h[i].physical() as u64); - } - if let Some(mut td) = self.transmit_ring_h.last_mut() { - td.ctrl.writef(EOR, true); - } - - // Unlock config - self.regs.cmd_9346.write(1 << 7 | 1 << 6); - - // Enable rx (bit 3) and tx (bit 2) - self.regs.cmd.writef(1 << 3 | 1 << 2, true); - - // Max RX packet size - self.regs.rms.write(0x1FF8); - - // Max TX packet size - self.regs.mtps.write(0x3B); - - // Set tx low priority buffer address - self.regs.tnpds[0].write(self.transmit_ring.physical() as u32); - self.regs.tnpds[1].write((self.transmit_ring.physical() >> 32) as u32); - - // Set tx high priority buffer address - self.regs.thpds[0].write(self.transmit_ring_h.physical() as u32); - self.regs.thpds[1].write((self.transmit_ring_h.physical() >> 32) as u32); - - // Set rx buffer address - self.regs.rdsar[0].write(self.receive_ring.physical() as u32); - self.regs.rdsar[1].write((self.receive_ring.physical() >> 32) as u32); - - // Disable timer interrupt - self.regs.timer_int.write(0); - - //Clear ISR - let isr = self.regs.isr.read(); - self.regs.isr.write(isr); - - // Interrupt on tx error (bit 3), tx ok (bit 2), rx error(bit 1), and rx ok (bit 0) - self.regs.imr.write(1 << 15 | 1 << 14 | 1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 3 | 1 << 2 | 1 << 1 | 1); - - // Set TX config - self.regs.tcr.write(0b11 << 24 | 0b111 << 8); - - // Set RX config - Accept broadcast (bit 3), multicast (bit 2), and unicast (bit 1) - self.regs.rcr.write(0xE70E); - - // Lock config - self.regs.cmd_9346.write(0); - } -} diff --git a/drivers/rtl8168d/src/main.rs b/drivers/rtl8168d/src/main.rs deleted file mode 100644 index 46a1b86..0000000 --- a/drivers/rtl8168d/src/main.rs +++ /dev/null @@ -1,141 +0,0 @@ -#![feature(asm)] - -extern crate dma; -extern crate event; -extern crate io; -extern crate netutils; -extern crate syscall; - -use std::cell::RefCell; -use std::env; -use std::fs::File; -use std::io::{Read, Write, Result}; -use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::sync::Arc; - -use event::EventQueue; -use syscall::{Packet, SchemeMut, MAP_WRITE}; -use syscall::error::EWOULDBLOCK; - -pub mod device; - -fn main() { - let mut args = env::args().skip(1); - - let mut name = args.next().expect("rtl8168d: no name provided"); - name.push_str("_rtl8168"); - - let bar_str = args.next().expect("rtl8168d: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("rtl8168d: failed to parse address"); - - let irq_str = args.next().expect("rtl8168d: no irq provided"); - let irq = irq_str.parse::().expect("rtl8168d: failed to parse irq"); - - print!("{}", format!(" + RTL8168 {} on: {:X}, IRQ: {}\n", name, bar, irq)); - - // Daemonize - if unsafe { syscall::clone(0).unwrap() } == 0 { - let socket_fd = syscall::open(":network", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("rtl8168d: failed to create network scheme"); - let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd) })); - - let mut irq_file = File::open(format!("irq:{}", irq)).expect("rtl8168d: failed to open IRQ file"); - - let address = unsafe { syscall::physmap(bar, 256, MAP_WRITE).expect("rtl8168d: failed to map address") }; - { - let device = Arc::new(RefCell::new(unsafe { device::Rtl8168::new(address).expect("rtl8168d: failed to allocate device") })); - - let mut event_queue = EventQueue::::new().expect("rtl8168d: 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(); - event_queue.add(irq_file.as_raw_fd(), move |_count: usize| -> Result> { - let mut irq = [0; 8]; - irq_file.read(&mut irq)?; - - let isr = unsafe { device_irq.borrow_mut().irq() }; - if isr != 0 { - 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.borrow_mut().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); - } - } - - let next_read = device_irq.borrow().next_read(); - if next_read > 0 { - return Ok(Some(next_read)); - } - } - Ok(None) - }).expect("rtl8168d: failed to catch events on IRQ file"); - - let socket_fd = socket.borrow().as_raw_fd(); - let socket_packet = socket.clone(); - event_queue.add(socket_fd, move |_count: usize| -> Result> { - loop { - let mut packet = Packet::default(); - if socket_packet.borrow_mut().read(&mut packet)? == 0 { - break; - } - - let a = packet.a; - device.borrow_mut().handle(&mut packet); - if packet.a == (-EWOULDBLOCK) as usize { - packet.a = a; - todo.borrow_mut().push(packet); - } else { - socket_packet.borrow_mut().write(&mut packet)?; - } - } - - let next_read = device.borrow().next_read(); - if next_read > 0 { - return Ok(Some(next_read)); - } - - Ok(None) - }).expect("rtl8168d: failed to catch events on IRQ file"); - - for event_count in event_queue.trigger_all(0).expect("rtl8168d: failed to trigger events") { - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ, - d: event_count - }).expect("rtl8168d: failed to write event"); - } - - loop { - let event_count = event_queue.run().expect("rtl8168d: failed to handle events"); - - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ, - d: event_count - }).expect("rtl8168d: failed to write event"); - } - } - unsafe { let _ = syscall::physunmap(address); } - } -} diff --git a/drivers/vesad/Cargo.toml b/drivers/vesad/Cargo.toml deleted file mode 100644 index a70dbf0..0000000 --- a/drivers/vesad/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "vesad" -version = "0.1.0" - -[dependencies] -orbclient = "0.2" -ransid = "0.2" -rusttype = { version = "0.2", optional = true } -redox_syscall = { path = "../../syscall" } - -[features] -default = [] diff --git a/drivers/vesad/src/display.rs b/drivers/vesad/src/display.rs deleted file mode 100644 index 829d6ba..0000000 --- a/drivers/vesad/src/display.rs +++ /dev/null @@ -1,246 +0,0 @@ -#[cfg(feature="rusttype")] -extern crate rusttype; - -use alloc::heap; -use std::{cmp, slice}; - -use primitive::{fast_set32, fast_set64, fast_copy, fast_copy64}; - -#[cfg(feature="rusttype")] -use self::rusttype::{Font, FontCollection, Scale, point}; - -#[cfg(not(feature="rusttype"))] -static FONT: &'static [u8] = include_bytes!("../../../res/fonts/unifont.font"); - -#[cfg(feature="rusttype")] -static FONT: &'static [u8] = include_bytes!("../../../res/fonts/DejaVuSansMono.ttf"); -#[cfg(feature="rusttype")] -static FONT_BOLD: &'static [u8] = include_bytes!("../../../res/fonts/DejaVuSansMono-Bold.ttf"); -#[cfg(feature="rusttype")] -static FONT_BOLD_ITALIC: &'static [u8] = include_bytes!("../../../res/fonts/DejaVuSansMono-BoldOblique.ttf"); -#[cfg(feature="rusttype")] -static FONT_ITALIC: &'static [u8] = include_bytes!("../../../res/fonts/DejaVuSansMono-Oblique.ttf"); - -/// A display -pub struct Display { - pub width: usize, - pub height: usize, - pub onscreen: &'static mut [u32], - pub offscreen: &'static mut [u32], - #[cfg(feature="rusttype")] - pub font: Font<'static>, - #[cfg(feature="rusttype")] - pub font_bold: Font<'static>, - #[cfg(feature="rusttype")] - pub font_bold_italic: Font<'static>, - #[cfg(feature="rusttype")] - pub font_italic: Font<'static> -} - -impl Display { - #[cfg(not(feature="rusttype"))] - pub fn new(width: usize, height: usize, onscreen: usize) -> Display { - let size = width * height; - let offscreen = unsafe { heap::allocate(size * 4, 4096) }; - unsafe { fast_set64(offscreen as *mut u64, 0, size/2) }; - Display { - width: width, - height: height, - onscreen: unsafe { slice::from_raw_parts_mut(onscreen as *mut u32, size) }, - offscreen: unsafe { slice::from_raw_parts_mut(offscreen as *mut u32, size) } - } - } - - #[cfg(feature="rusttype")] - pub fn new(width: usize, height: usize, onscreen: usize) -> Display { - let size = width * height; - let offscreen = unsafe { heap::allocate(size * 4, 4096) }; - unsafe { fast_set64(offscreen as *mut u64, 0, size/2) }; - Display { - width: width, - height: height, - onscreen: unsafe { slice::from_raw_parts_mut(onscreen as *mut u32, size) }, - offscreen: unsafe { slice::from_raw_parts_mut(offscreen as *mut u32, size) }, - font: FontCollection::from_bytes(FONT).into_font().unwrap(), - font_bold: FontCollection::from_bytes(FONT_BOLD).into_font().unwrap(), - font_bold_italic: FontCollection::from_bytes(FONT_BOLD_ITALIC).into_font().unwrap(), - font_italic: FontCollection::from_bytes(FONT_ITALIC).into_font().unwrap() - } - } - - /// Draw a rectangle - pub fn rect(&mut self, x: usize, y: usize, w: usize, h: usize, color: u32) { - let start_y = cmp::min(self.height - 1, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width - 1, x); - let len = cmp::min(self.width, x + w) - start_x; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - - let stride = self.width * 4; - - let offset = y * stride + start_x * 4; - offscreen_ptr += offset; - - let mut rows = end_y - start_y; - while rows > 0 { - unsafe { - fast_set32(offscreen_ptr as *mut u32, color, len); - } - offscreen_ptr += stride; - rows -= 1; - } - } - - /// Invert a rectangle - pub fn invert(&mut self, x: usize, y: usize, w: usize, h: usize) { - let start_y = cmp::min(self.height - 1, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width - 1, x); - let len = cmp::min(self.width, x + w) - start_x; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - - let stride = self.width * 4; - - let offset = y * stride + start_x * 4; - offscreen_ptr += offset; - - let mut rows = end_y - start_y; - while rows > 0 { - let mut row_ptr = offscreen_ptr; - let mut cols = len; - while cols > 0 { - unsafe { - let color = *(row_ptr as *mut u32); - *(row_ptr as *mut u32) = !color; - } - row_ptr += 4; - cols -= 1; - } - offscreen_ptr += stride; - rows -= 1; - } - } - - /// Draw a character - #[cfg(not(feature="rusttype"))] - pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, _bold: bool, _italic: bool) { - if x + 8 <= self.width && y + 16 <= self.height { - let mut dst = self.offscreen.as_mut_ptr() as usize + (y * self.width + x) * 4; - - let font_i = 16 * (character as usize); - if font_i + 16 <= FONT.len() { - for row in 0..16 { - let row_data = FONT[font_i + row]; - for col in 0..8 { - if (row_data >> (7 - col)) & 1 == 1 { - unsafe { *((dst + col * 4) as *mut u32) = color; } - } - } - dst += self.width * 4; - } - } - } - } - - /// Draw a character - #[cfg(feature="rusttype")] - pub fn char(&mut self, x: usize, y: usize, character: char, color: u32, bold: bool, italic: bool) { - let width = self.width; - let height = self.height; - let offscreen = self.offscreen.as_mut_ptr() as usize; - - let font = if bold && italic { - &self.font_bold_italic - } else if bold { - &self.font_bold - } else if italic { - &self.font_italic - } else { - &self.font - }; - - if let Some(glyph) = font.glyph(character){ - let scale = Scale::uniform(16.0); - let v_metrics = font.v_metrics(scale); - let point = point(0.0, v_metrics.ascent); - let glyph = glyph.scaled(scale).positioned(point); - if let Some(bb) = glyph.pixel_bounding_box() { - glyph.draw(|off_x, off_y, v| { - let off_x = x + (off_x as i32 + bb.min.x) as usize; - let off_y = y + (off_y as i32 + bb.min.y) as usize; - // There's still a possibility that the glyph clips the boundaries of the bitmap - if off_x < width && off_y < height { - if v > 0.0 { - let f_a = (v * 255.0) as u32; - let f_r = (((color >> 16) & 0xFF) * f_a)/255; - let f_g = (((color >> 8) & 0xFF) * f_a)/255; - let f_b = ((color & 0xFF) * f_a)/255; - - let offscreen_ptr = (offscreen + (off_y * width + off_x) * 4) as *mut u32; - - let bg = unsafe { *offscreen_ptr }; - - let b_a = 255 - f_a; - let b_r = (((bg >> 16) & 0xFF) * b_a)/255; - let b_g = (((bg >> 8) & 0xFF) * b_a)/255; - let b_b = ((bg & 0xFF) * b_a)/255; - - let c = ((f_r + b_r) << 16) | ((f_g + b_g) << 8) | (f_b + b_b); - - unsafe { *offscreen_ptr = c; } - } - } - }); - } - } - } - - /// Scroll display - pub fn scroll(&mut self, rows: usize, color: u32) { - let data = (color as u64) << 32 | color as u64; - - let width = self.width/2; - let height = self.height; - if rows > 0 && rows < height { - let off1 = rows * width; - let off2 = height * width - off1; - unsafe { - let data_ptr = self.offscreen.as_mut_ptr() as *mut u64; - fast_copy64(data_ptr, data_ptr.offset(off1 as isize), off2); - fast_set64(data_ptr.offset(off2 as isize), data, off1); - } - } - } - - /// Copy from offscreen to onscreen - pub fn sync(&mut self, x: usize, y: usize, w: usize, h: usize) { - let start_y = cmp::min(self.height - 1, y); - let end_y = cmp::min(self.height, y + h); - - let start_x = cmp::min(self.width - 1, x); - let len = (cmp::min(self.width, x + w) - start_x) * 4; - - let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; - let mut onscreen_ptr = self.onscreen.as_mut_ptr() as usize; - - let stride = self.width * 4; - - let offset = y * stride + start_x * 4; - offscreen_ptr += offset; - onscreen_ptr += offset; - - let mut rows = end_y - start_y; - while rows > 0 { - unsafe { - fast_copy(onscreen_ptr as *mut u8, offscreen_ptr as *const u8, len); - } - offscreen_ptr += stride; - onscreen_ptr += stride; - rows -= 1; - } - } -} diff --git a/drivers/vesad/src/main.rs b/drivers/vesad/src/main.rs deleted file mode 100644 index 7f4347a..0000000 --- a/drivers/vesad/src/main.rs +++ /dev/null @@ -1,109 +0,0 @@ -#![feature(alloc)] -#![feature(asm)] -#![feature(heap_api)] - -extern crate alloc; -extern crate orbclient; -extern crate syscall; - -use std::{env, mem}; -use std::fs::File; -use std::io::{Read, Write}; -use syscall::{physmap, physunmap, Packet, SchemeMut, EVENT_READ, MAP_WRITE, MAP_WRITE_COMBINE}; - -use mode_info::VBEModeInfo; -use primitive::fast_set64; -use scheme::DisplayScheme; - -pub mod display; -pub mod mode_info; -pub mod primitive; -pub mod scheme; -pub mod screen; - -fn main() { - let mut spec = Vec::new(); - - for arg in env::args().skip(1) { - if arg == "T" { - spec.push(false); - } else if arg == "G" { - spec.push(true); - } else { - println!("vesad: unknown screen type: {}", arg); - } - } - - let width; - let height; - let physbaseptr; - - { - let mode_info = unsafe { &*(physmap(0x5200, 4096, 0).expect("vesad: failed to map VBE info") as *const VBEModeInfo) }; - - width = mode_info.xresolution as usize; - height = mode_info.yresolution as usize; - physbaseptr = mode_info.physbaseptr as usize; - - unsafe { let _ = physunmap(mode_info as *const _ as usize); } - } - - if physbaseptr > 0 { - // Daemonize - if unsafe { syscall::clone(0).unwrap() } == 0 { - let mut socket = File::create(":display").expect("vesad: failed to create display scheme"); - - let size = width * height; - - let onscreen = unsafe { physmap(physbaseptr, size * 4, MAP_WRITE | MAP_WRITE_COMBINE).expect("vesad: failed to map VBE LFB") }; - unsafe { fast_set64(onscreen as *mut u64, 0, size/2) }; - - let mut scheme = DisplayScheme::new(width, height, onscreen, &spec); - - let mut blocked = Vec::new(); - loop { - let mut packet = Packet::default(); - socket.read(&mut packet).expect("vesad: failed to read display scheme"); - - // If it is a read packet, and there is no data, block it. Otherwise, handle packet - if packet.a == syscall::number::SYS_READ && packet.d > 0 && scheme.will_block(packet.b) { - blocked.push(packet); - } else { - scheme.handle(&mut packet); - socket.write(&packet).expect("vesad: failed to write display scheme"); - } - - // If there are blocked readers, and data is available, handle them - { - let mut i = 0; - while i < blocked.len() { - if ! scheme.will_block(blocked[i].b) { - let mut packet = blocked.remove(i); - scheme.handle(&mut packet); - socket.write(&packet).expect("vesad: failed to write display scheme"); - } else { - i += 1; - } - } - } - - for (screen_id, screen) in scheme.screens.iter() { - if ! screen.will_block() { - let event_packet = Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: *screen_id, - c: EVENT_READ, - d: mem::size_of::() - }; - - socket.write(&event_packet).expect("vesad: failed to write display event"); - } - } - } - } - } -} diff --git a/drivers/vesad/src/mode_info.rs b/drivers/vesad/src/mode_info.rs deleted file mode 100644 index 7d59af6..0000000 --- a/drivers/vesad/src/mode_info.rs +++ /dev/null @@ -1,37 +0,0 @@ -/// The info of the VBE mode -#[derive(Copy, Clone, Default, Debug)] -#[repr(packed)] -pub struct VBEModeInfo { - attributes: u16, - win_a: u8, - win_b: u8, - granularity: u16, - winsize: u16, - segment_a: u16, - segment_b: u16, - winfuncptr: u32, - bytesperscanline: u16, - pub xresolution: u16, - pub yresolution: u16, - xcharsize: u8, - ycharsize: u8, - numberofplanes: u8, - bitsperpixel: u8, - numberofbanks: u8, - memorymodel: u8, - banksize: u8, - numberofimagepages: u8, - unused: u8, - redmasksize: u8, - redfieldposition: u8, - greenmasksize: u8, - greenfieldposition: u8, - bluemasksize: u8, - bluefieldposition: u8, - rsvdmasksize: u8, - rsvdfieldposition: u8, - directcolormodeinfo: u8, - pub physbaseptr: u32, - offscreenmemoryoffset: u32, - offscreenmemsize: u16, -} diff --git a/drivers/vesad/src/primitive.rs b/drivers/vesad/src/primitive.rs deleted file mode 100644 index 16c2536..0000000 --- a/drivers/vesad/src/primitive.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[cfg(target_arch = "x86_64")] -#[inline(always)] -#[cold] -pub unsafe fn fast_copy(dst: *mut u8, src: *const u8, len: usize) { - asm!("cld - rep movsb" - : - : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len) - : "cc", "memory", "rdi", "rsi", "rcx" - : "intel", "volatile"); -} - -#[cfg(target_arch = "x86_64")] -#[inline(always)] -#[cold] -pub unsafe fn fast_copy64(dst: *mut u64, src: *const u64, len: usize) { - asm!("cld - rep movsq" - : - : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len) - : "cc", "memory", "rdi", "rsi", "rcx" - : "intel", "volatile"); -} - -#[cfg(target_arch = "x86_64")] -#[inline(always)] -#[cold] -pub unsafe fn fast_set32(dst: *mut u32, src: u32, len: usize) { - asm!("cld - rep stosd" - : - : "{rdi}"(dst as usize), "{eax}"(src), "{rcx}"(len) - : "cc", "memory", "rdi", "rcx" - : "intel", "volatile"); -} - -#[cfg(target_arch = "x86_64")] -#[inline(always)] -#[cold] -pub unsafe fn fast_set64(dst: *mut u64, src: u64, len: usize) { - asm!("cld - rep stosq" - : - : "{rdi}"(dst as usize), "{rax}"(src), "{rcx}"(len) - : "cc", "memory", "rdi", "rcx" - : "intel", "volatile"); -} diff --git a/drivers/vesad/src/scheme.rs b/drivers/vesad/src/scheme.rs deleted file mode 100644 index e376bf7..0000000 --- a/drivers/vesad/src/scheme.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::collections::BTreeMap; -use std::{mem, slice, str}; - -use orbclient::{Event, EventOption}; -use syscall::{Result, Error, EACCES, EBADF, ENOENT, SchemeMut}; - -use display::Display; -use screen::{Screen, GraphicScreen, TextScreen}; - -pub struct DisplayScheme { - active: usize, - pub screens: BTreeMap> -} - -impl DisplayScheme { - pub fn new(width: usize, height: usize, onscreen: usize, spec: &[bool]) -> DisplayScheme { - let mut screens: BTreeMap> = BTreeMap::new(); - - let mut screen_i = 1; - for &screen_type in spec.iter() { - if screen_type { - screens.insert(screen_i, Box::new(GraphicScreen::new(Display::new(width, height, onscreen)))); - } else { - screens.insert(screen_i, Box::new(TextScreen::new(Display::new(width, height, onscreen)))); - } - screen_i += 1; - } - - DisplayScheme { - active: 1, - screens: screens - } - } - - pub fn will_block(&self, id: usize) -> bool { - if let Some(screen) = self.screens.get(&id) { - screen.will_block() - } else { - false - } - } -} - -impl SchemeMut for DisplayScheme { - fn open(&mut self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { - if path == b"input" { - if uid == 0 { - Ok(0) - } else { - Err(Error::new(EACCES)) - } - } else { - let path_str = str::from_utf8(path).unwrap_or("").trim_matches('/'); - let mut parts = path_str.split('/'); - let id = parts.next().unwrap_or("").parse::().unwrap_or(0); - if self.screens.contains_key(&id) { - for cmd in parts { - if cmd == "activate" { - self.active = id; - } - } - Ok(id) - } else { - Err(Error::new(ENOENT)) - } - } - } - - fn dup(&mut self, id: usize, _buf: &[u8]) -> Result { - Ok(id) - } - - fn fevent(&mut self, id: usize, flags: usize) -> Result { - if let Some(mut screen) = self.screens.get_mut(&id) { - screen.event(flags).and(Ok(id)) - } else { - Err(Error::new(EBADF)) - } - } - - fn fmap(&mut self, id: usize, offset: usize, size: usize) -> Result { - if let Some(screen) = self.screens.get(&id) { - screen.map(offset, size) - } else { - Err(Error::new(EBADF)) - } - } - - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { - let path_str = if id == 0 { - format!("display:input") - } else if let Some(screen) = self.screens.get(&id) { - format!("display:{}/{}/{}", id, screen.width(), screen.height()) - } else { - return Err(Error::new(EBADF)); - }; - - let path = path_str.as_bytes(); - - let mut i = 0; - while i < buf.len() && i < path.len() { - buf[i] = path[i]; - i += 1; - } - - Ok(i) - } - - fn fsync(&mut self, id: usize) -> Result { - if let Some(mut screen) = self.screens.get_mut(&id) { - if id == self.active { - screen.sync(); - } - Ok(0) - } else { - Err(Error::new(EBADF)) - } - } - - fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { - if let Some(mut screen) = self.screens.get_mut(&id) { - screen.read(buf) - } else { - Err(Error::new(EBADF)) - } - } - - fn write(&mut self, id: usize, buf: &[u8]) -> Result { - if id == 0 { - if buf.len() == 1 && buf[0] >= 0xF4 { - let new_active = (buf[0] - 0xF4) as usize + 1; - if let Some(mut screen) = self.screens.get_mut(&new_active) { - self.active = new_active; - screen.redraw(); - } - Ok(1) - } else { - 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) = self.screens.get_mut(&new_active) { - self.active = new_active; - screen.redraw(); - } - } else { - if let Some(mut screen) = self.screens.get_mut(&self.active) { - screen.input(event); - } - } - } - - Ok(events.len() * mem::size_of::()) - } - } else if let Some(mut screen) = self.screens.get_mut(&id) { - screen.write(buf, id == self.active) - } else { - Err(Error::new(EBADF)) - } - } - - fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result { - if let Some(mut screen) = self.screens.get_mut(&id) { - screen.seek(pos, whence) - } else { - Err(Error::new(EBADF)) - } - } - - fn close(&mut self, _id: usize) -> Result { - Ok(0) - } -} diff --git a/drivers/vesad/src/screen/graphic.rs b/drivers/vesad/src/screen/graphic.rs deleted file mode 100644 index b911922..0000000 --- a/drivers/vesad/src/screen/graphic.rs +++ /dev/null @@ -1,126 +0,0 @@ -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 map(&self, offset: usize, size: usize) -> Result { - if offset + size <= self.display.offscreen.len() * 4 { - Ok(self.display.offscreen.as_ptr() as usize + offset) - } else { - Err(Error::new(EINVAL)) - } - } - - 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 deleted file mode 100644 index 9909694..0000000 --- a/drivers/vesad/src/screen/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub use self::graphic::GraphicScreen; -pub use self::text::TextScreen; - -use orbclient::Event; -use syscall::Result; - -mod graphic; -mod text; - -pub trait Screen { - fn width(&self) -> usize; - - fn height(&self) -> usize; - - fn event(&mut self, flags: usize) -> Result; - - fn map(&self, offset: usize, size: 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 deleted file mode 100644 index ccc259d..0000000 --- a/drivers/vesad/src/screen/text.rs +++ /dev/null @@ -1,233 +0,0 @@ -extern crate ransid; - -use std::collections::{BTreeSet, VecDeque}; - -use orbclient::{Event, EventOption}; -use syscall::error::*; - -use display::Display; -use screen::Screen; - -pub struct TextScreen { - pub console: ransid::Console, - pub display: Display, - pub changed: BTreeSet, - pub ctrl: bool, - pub input: VecDeque, - pub end_of_input: bool, - pub cooked: VecDeque, - pub requested: usize -} - -impl TextScreen { - pub fn new(display: Display) -> TextScreen { - TextScreen { - 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 map(&self, offset: usize, size: usize) -> Result { - Err(Error::new(EBADF)) - } - - 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 { - 0x0E => { // Backspace - buf.extend_from_slice(b"\x7F"); - }, - 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 - } - - if self.console.raw_mode { - for &b in buf.iter() { - self.input.push_back(b); - } - } else { - for &b in buf.iter() { - match b { - b'\x03' => { - self.end_of_input = true; - let _ = self.write(b"^C\n", true); - }, - b'\x08' | b'\x7F' => { - if let Some(_c) = self.cooked.pop_back() { - let _ = self.write(b"\x08", true); - } - }, - b'\x1B' => { - let _ = self.write(b"^[", true); - }, - b'\n' | b'\r' => { - self.cooked.push_back(b); - while let Some(c) = self.cooked.pop_front() { - self.input.push_back(c); - } - let _ = self.write(b"\n", true); - }, - _ => { - self.cooked.push_back(b); - let _ = self.write(&[b], true); - } - } - } - } - } - - fn read(&mut self, buf: &mut [u8]) -> Result { - let mut i = 0; - - while i < buf.len() && ! self.input.is_empty() { - buf[i] = self.input.pop_front().unwrap(); - i += 1; - } - - if i == 0 { - self.end_of_input = false; - } - - Ok(i) - } - - fn will_block(&self) -> bool { - self.input.is_empty() && ! self.end_of_input - } - - 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; - self.display.invert(x * 8, y * 16, 8, 16); - self.changed.insert(y); - } - - { - let display = &mut self.display; - let changed = &mut self.changed; - self.console.write(buf, |event| { - match event { - ransid::Event::Char { x, y, c, color, bold, .. } => { - display.char(x * 8, y * 16, c, color.data, bold, false); - changed.insert(y); - }, - 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); - } - }, - ransid::Event::Scroll { rows, color } => { - display.scroll(rows * 16, color.data); - for y in 0..display.height/16 { - changed.insert(y); - } - } - } - }); - } - - 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; - self.display.invert(x * 8, y * 16, 8, 16); - self.changed.insert(y); - } - - if ! self.console.raw_mode && sync { - self.sync(); - } - - Ok(buf.len()) - } - - 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); - } - self.changed.clear(); - } - - fn redraw(&mut self) { - let width = self.display.width; - let height = self.display.height; - self.display.sync(0, 0, width, height); - self.changed.clear(); - } -} From 5776b0b452be65bb4cff1d7b73b2a27d7aefebbf Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 5 Jan 2017 11:26:32 -0700 Subject: [PATCH 5/6] Move bootloader to submodule --- .gitmodules | 3 + bootloader | 1 + bootloader/arm/start.s | 17 -- bootloader/x86_64/bootsector.asm | 188 ---------------------- bootloader/x86_64/config.asm | 18 --- bootloader/x86_64/descriptor_flags.inc | 46 ------ bootloader/x86_64/gdt_entry.inc | 8 - bootloader/x86_64/harddrive.asm | 21 --- bootloader/x86_64/initialize.asm | 78 ---------- bootloader/x86_64/livedisk.asm | 19 --- bootloader/x86_64/memory_map.asm | 32 ---- bootloader/x86_64/print16.asm | 65 -------- bootloader/x86_64/startup-common.asm | 110 ------------- bootloader/x86_64/startup-i386.asm | 148 ------------------ bootloader/x86_64/startup-x86_64.asm | 179 --------------------- bootloader/x86_64/unreal.asm | 54 ------- bootloader/x86_64/vesa.asm | 207 ------------------------- bootloader/x86_64/vesa.inc | 90 ----------- 18 files changed, 4 insertions(+), 1280 deletions(-) create mode 160000 bootloader delete mode 100644 bootloader/arm/start.s delete mode 100644 bootloader/x86_64/bootsector.asm delete mode 100644 bootloader/x86_64/config.asm delete mode 100644 bootloader/x86_64/descriptor_flags.inc delete mode 100644 bootloader/x86_64/gdt_entry.inc delete mode 100644 bootloader/x86_64/harddrive.asm delete mode 100644 bootloader/x86_64/initialize.asm delete mode 100644 bootloader/x86_64/livedisk.asm delete mode 100644 bootloader/x86_64/memory_map.asm delete mode 100644 bootloader/x86_64/print16.asm delete mode 100644 bootloader/x86_64/startup-common.asm delete mode 100644 bootloader/x86_64/startup-i386.asm delete mode 100644 bootloader/x86_64/startup-x86_64.asm delete mode 100644 bootloader/x86_64/unreal.asm delete mode 100644 bootloader/x86_64/vesa.asm delete mode 100644 bootloader/x86_64/vesa.inc diff --git a/.gitmodules b/.gitmodules index c5a24df..98d2765 100644 --- a/.gitmodules +++ b/.gitmodules @@ -64,3 +64,6 @@ [submodule "drivers"] path = drivers url = https://github.com/redox-os/drivers.git +[submodule "bootloader"] + path = bootloader + url = https://github.com/redox-os/bootloader.git diff --git a/bootloader b/bootloader new file mode 160000 index 0000000..7639aef --- /dev/null +++ b/bootloader @@ -0,0 +1 @@ +Subproject commit 7639aef32ed5ffd272701d955147b8b50aad53e0 diff --git a/bootloader/arm/start.s b/bootloader/arm/start.s deleted file mode 100644 index 8495600..0000000 --- a/bootloader/arm/start.s +++ /dev/null @@ -1,17 +0,0 @@ -interrupt_vector_table: - b . @ Reset - b . - b . @ SWI instruction - b . - b . - b . - b . - b . - -.comm stack, 0x10000 @ Reserve 64k stack in the BSS -_start: - .globl _start - ldr sp, =stack+0x10000 @ Set up the stack - bl kstart @ Jump to the main function -1: - b 1b @ Halt diff --git a/bootloader/x86_64/bootsector.asm b/bootloader/x86_64/bootsector.asm deleted file mode 100644 index b66184e..0000000 --- a/bootloader/x86_64/bootsector.asm +++ /dev/null @@ -1,188 +0,0 @@ -ORG 0x7C00 -SECTION .text -USE16 - -boot: ; dl comes with disk - ; initialize segment registers - xor ax, ax - mov ds, ax - mov es, ax - mov ss, ax - - ; initialize stack - mov sp, 0x7C00 - - ; initialize CS - push ax - push word .set_cs - retf - -.set_cs: - - ; save disk number - mov [disk], dl - - mov si, name - call print - call print_line - - mov bx, (startup_start - boot) / 512 - call print_num - call print_line - - mov bx, startup_start - call print_num - call print_line - - mov eax, (startup_start - boot) / 512 - mov bx, startup_start - mov cx, (startup_end - startup_start) / 512 - xor dx, dx - call load - - mov si, finished - call print - call print_line - - jmp startup - -; load some sectors from disk to a buffer in memory -; buffer has to be below 1MiB -; IN -; ax: start sector -; bx: offset of buffer -; cx: number of sectors (512 Bytes each) -; dx: segment of buffer -; CLOBBER -; ax, bx, cx, dx, si -; TODO rewrite to (eventually) move larger parts at once -; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end) -load: - cmp cx, 127 - jbe .good_size - - pusha - mov cx, 127 - call load - popa - add ax, 127 - add dx, 127 * 512 / 16 - sub cx, 127 - - jmp load -.good_size: - mov [DAPACK.addr], eax - mov [DAPACK.buf], bx - mov [DAPACK.count], cx - mov [DAPACK.seg], dx - - call print_dapack - - mov dl, [disk] - mov si, DAPACK - mov ah, 0x42 - int 0x13 - jc error - ret - - ; store some sectors to disk from a buffer in memory - ; buffer has to be below 1MiB - ; IN - ; ax: start sector - ; bx: offset of buffer - ; cx: number of sectors (512 Bytes each) - ; dx: segment of buffer - ; CLOBBER - ; ax, bx, cx, dx, si - ; TODO rewrite to (eventually) move larger parts at once - ; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end) - store: - cmp cx, 127 - jbe .good_size - - pusha - mov cx, 127 - call store - popa - add ax, 127 - add dx, 127 * 512 / 16 - sub cx, 127 - - jmp store - .good_size: - mov [DAPACK.addr], eax - mov [DAPACK.buf], bx - mov [DAPACK.count], cx - mov [DAPACK.seg], dx - - call print_dapack - - mov dl, [disk] - mov si, DAPACK - mov ah, 0x43 - int 0x13 - jc error - ret - -print_dapack: - mov bx, [DAPACK.addr + 2] - call print_num - - mov bx, [DAPACK.addr] - call print_num - - mov al, '#' - call print_char - - mov bx, [DAPACK.count] - call print_num - - mov al, ' ' - call print_char - - mov bx, [DAPACK.seg] - call print_num - - mov al, ':' - call print_char - - mov bx, [DAPACK.buf] - call print_num - - jmp print_line - -error: - mov bh, 0 - mov bl, ah - call print_num - - mov al, ' ' - call print_char - - mov si, errored - call print - call print_line -.halt: - cli - hlt - jmp .halt - -%include "print16.asm" - -name: db "Redox Loader - Stage One",0 -errored: db "Could not read disk",0 -finished: db "Redox Loader - Stage Two",0 - -disk: db 0 - -DAPACK: - db 0x10 - db 0 -.count: dw 0 ; int 13 resets this to # of blocks actually read/written -.buf: dw 0 ; memory buffer destination address (0:7c00) -.seg: dw 0 ; in memory page zero -.addr: dq 0 ; put the lba to read in this spot - -times 510-($-$$) db 0 -db 0x55 -db 0xaa diff --git a/bootloader/x86_64/config.asm b/bootloader/x86_64/config.asm deleted file mode 100644 index 8e3b220..0000000 --- a/bootloader/x86_64/config.asm +++ /dev/null @@ -1,18 +0,0 @@ -SECTION .text -USE16 - -align 512, db 0 - -config: - .xres: dw 0 - .yres: dw 0 - -times 512 - ($ - config) db 0 - -save_config: - mov eax, (config - boot) / 512 - mov bx, config - mov cx, 1 - xor dx, dx - call store - ret diff --git a/bootloader/x86_64/descriptor_flags.inc b/bootloader/x86_64/descriptor_flags.inc deleted file mode 100644 index 210c98d..0000000 --- a/bootloader/x86_64/descriptor_flags.inc +++ /dev/null @@ -1,46 +0,0 @@ -attrib: - .present equ 1 << 7 - .ring1 equ 1 << 5 - .ring2 equ 1 << 6 - .ring3 equ 1 << 5 | 1 << 6 - .user equ 1 << 4 -;user - .code equ 1 << 3 -; code - .conforming equ 1 << 2 - .readable equ 1 << 1 -; data - .expand_down equ 1 << 2 - .writable equ 1 << 1 - .accessed equ 1 << 0 -;system -; legacy - .tssAvailabe16 equ 0x1 - .ldt equ 0x2 - .tssBusy16 equ 0x3 - .call16 equ 0x4 - .task equ 0x5 - .interrupt16 equ 0x6 - .trap16 equ 0x7 - .tssAvailabe32 equ 0x9 - .tssBusy32 equ 0xB - .call32 equ 0xC - .interrupt32 equ 0xE - .trap32 equ 0xF -; long mode - .ldt32 equ 0x2 - .tssAvailabe64 equ 0x9 - .tssBusy64 equ 0xB - .call64 equ 0xC - .interrupt64 equ 0xE - .trap64 equ 0xF - -flags: - .granularity equ 1 << 7 - .available equ 1 << 4 -;user - .default_operand_size equ 1 << 6 -; code - .long_mode equ 1 << 5 -; data - .reserved equ 1 << 5 diff --git a/bootloader/x86_64/gdt_entry.inc b/bootloader/x86_64/gdt_entry.inc deleted file mode 100644 index 861a78b..0000000 --- a/bootloader/x86_64/gdt_entry.inc +++ /dev/null @@ -1,8 +0,0 @@ -struc GDTEntry - .limitl resw 1 - .basel resw 1 - .basem resb 1 - .attribute resb 1 - .flags__limith resb 1 - .baseh resb 1 -endstruc diff --git a/bootloader/x86_64/harddrive.asm b/bootloader/x86_64/harddrive.asm deleted file mode 100644 index 052dfe8..0000000 --- a/bootloader/x86_64/harddrive.asm +++ /dev/null @@ -1,21 +0,0 @@ -%include "bootsector.asm" - -startup_start: -%ifdef ARCH_i386 - %include "startup-i386.asm" -%endif - -%ifdef ARCH_x86_64 - %include "startup-x86_64.asm" -%endif -align 512, db 0 -startup_end: - -kernel_file: - incbin "build/kernel/kernel" - align 512, db 0 -.end: -.length equ kernel_file.end - kernel_file -.length_sectors equ .length / 512 - -incbin "build/filesystem.bin" diff --git a/bootloader/x86_64/initialize.asm b/bootloader/x86_64/initialize.asm deleted file mode 100644 index e723d37..0000000 --- a/bootloader/x86_64/initialize.asm +++ /dev/null @@ -1,78 +0,0 @@ -SECTION .text -USE16 - -initialize: -.fpu: ;enable fpu - mov eax, cr0 - and al, 11110011b - or al, 00100010b - mov cr0, eax - mov eax, cr4 - or eax, 0x200 - mov cr4, eax - fninit - ret - -.sse: ;enable sse - mov eax, cr4 - or ax, 0000011000000000b - mov cr4, eax - ret - -;PIT Frequency -;If using nanoseconds, to minimize drift, one should find a frequency as close to an integer nanosecond value in wavelength -;Divider Hz Nanoseconds Properties -;2685 444.38795779019242706393 2250286.00003631746492922946 Best For Context Switching -;5370 222.19397889509621353196 4500572.00007263492985856020 -;21029 56.73981961418358774390 17624306.99991199998882825455 -;23714 50.31549576902532962244 19874592.99994831745375667118 -;26399 45.19798729749864262535 22124878.99998463491868476373 -;29084 41.02536331545408701233 24375165.00002095238361424615 -;31769 37.55804925136663623868 26625451.00005726984854313455 -;34454 34.63115071302799868423 28875737.00009358731347639618 -;50113 23.80982313305263437963 41999471.99993295237244784676 -;52798 22.59899364874932131267 44249757.99996926983737931766 -;55483 21.50535599492937776736 46500044.00000558730230583335 Lowest Drift -;58168 20.51268165772704350616 48750330.00004190476724037528 -;60853 19.60760630809765610021 51000616.00007822223218031738 - -.pit: - ;initialize the PIT - mov ax, 2685 ;this is the divider for the PIT - out 0x40, al - rol ax, 8 - out 0x40, al - ;DISABLED ;enable rtc interrupt - ;mov al, 0xB - ;out 0x70, al - ;rol ax, 8 - ;in al, 0x71 - ;rol ax, 8 - ;out 0x70, al - ;rol ax, 8 - ;or al, 0x40 - ;out 0x71, al - ret - -.pic: ;sets up IRQs at int 20-2F - mov al, 0x11 - out 0x20, al - out 0xA0, al - mov al, 0x20 ;IRQ0 vector - out 0x21, al - mov al, 0x28 ;IRQ8 vector - out 0xA1, al - mov al, 4 - out 0x21, al - mov al, 2 - out 0xA1, al - mov al, 1 - out 0x21, al - out 0xA1, al - xor al, al ;no IRQ masks - out 0x21, al - out 0xA1, al - mov al, 0x20 ;reset PIC's - out 0xA0, al - out 0x20, al - ret diff --git a/bootloader/x86_64/livedisk.asm b/bootloader/x86_64/livedisk.asm deleted file mode 100644 index e1cd643..0000000 --- a/bootloader/x86_64/livedisk.asm +++ /dev/null @@ -1,19 +0,0 @@ -%include "bootsector.asm" - -startup_start: -%ifdef ARCH_i386 - %include "startup-i386.asm" -%endif - -%ifdef ARCH_x86_64 - %include "startup-x86_64.asm" -%endif -align 512, db 0 -startup_end: - -kernel_file: - incbin "build/kernel/kernel_live" - align 512, db 0 -.end: -.length equ kernel_file.end - kernel_file -.length_sectors equ .length / 512 diff --git a/bootloader/x86_64/memory_map.asm b/bootloader/x86_64/memory_map.asm deleted file mode 100644 index a5cdc70..0000000 --- a/bootloader/x86_64/memory_map.asm +++ /dev/null @@ -1,32 +0,0 @@ -SECTION .text -USE16 -;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader) -memory_map: -.start equ 0x0500 -.end equ 0x5000 -.length equ .end - .start - - xor eax, eax - mov di, .start - mov ecx, .length / 4 ; moving 4 Bytes at once - cld - rep stosd - - mov di, .start - mov edx, 0x534D4150 - xor ebx, ebx -.lp: - mov eax, 0xE820 - mov ecx, 24 - - int 0x15 - jc .done ; Error or finished - - cmp ebx, 0 - je .done ; Finished - - add di, 24 - cmp di, .end - jb .lp ; Still have buffer space -.done: - ret diff --git a/bootloader/x86_64/print16.asm b/bootloader/x86_64/print16.asm deleted file mode 100644 index dada0fd..0000000 --- a/bootloader/x86_64/print16.asm +++ /dev/null @@ -1,65 +0,0 @@ -SECTION .text -USE16 -; provide function for printing in x86 real mode - -; print a string and a newline -; IN -; si: points at zero-terminated String -; CLOBBER -; ax -print_line: - mov al, 13 - call print_char - mov al, 10 - jmp print_char - -; print a string -; IN -; si: points at zero-terminated String -; CLOBBER -; ax -print: - cld -.loop: - lodsb - test al, al - jz .done - call print_char - jmp .loop -.done: - ret - -; print a character -; IN -; al: character to print -; CLOBBER -; ah -print_char: - mov ah, 0x0e - int 0x10 - ret - -; print a number in hex -; IN -; bx: the number -; CLOBBER -; cx, ax -print_num: - mov cx, 4 -.lp: - mov al, bh - shr al, 4 - - cmp al, 0xA - jb .below_0xA - - add al, 'A' - 0xA - '0' -.below_0xA: - add al, '0' - - call print_char - - shl bx, 4 - loop .lp - - ret diff --git a/bootloader/x86_64/startup-common.asm b/bootloader/x86_64/startup-common.asm deleted file mode 100644 index 9677bbc..0000000 --- a/bootloader/x86_64/startup-common.asm +++ /dev/null @@ -1,110 +0,0 @@ -SECTION .text -USE16 - -startup: - ; enable A20-Line via IO-Port 92, might not work on all motherboards - in al, 0x92 - or al, 2 - out 0x92, al - -; loading kernel to 1MiB -; move part of kernel to startup_end via bootsector#load and then copy it up -; repeat until all of the kernel is loaded - -; buffersize in multiple of sectors (512 Bytes) -; min 1 -; max (0x70000 - startup_end) / 512 -buffer_size_sectors equ 127 -; buffer size in Bytes -buffer_size_bytes equ buffer_size_sectors * 512 - -kernel_base equ 0x100000 - - ; how often do we need to call load and move memory - mov ecx, kernel_file.length_sectors / buffer_size_sectors - - mov eax, (kernel_file - boot) / 512 - mov edi, kernel_base - cld -.lp: - ; saving counter - push cx - - ; populating buffer - mov cx, buffer_size_sectors - mov bx, kernel_file - mov dx, 0x0 - - push edi - push eax - call load - - ; moving buffer - call unreal - pop eax - pop edi - - mov esi, kernel_file - mov ecx, buffer_size_bytes / 4 - a32 rep movsd - - ; preparing next iteration - add eax, buffer_size_sectors - - pop cx - loop .lp - - ; load the part of the kernel that does not fill the buffer completely - mov cx, kernel_file.length_sectors % buffer_size_sectors - test cx, cx - jz finished_loading ; if cx = 0 => skip - - mov bx, kernel_file - mov dx, 0x0 - call load - - ; moving remnants of kernel - call unreal - - mov esi, kernel_file - mov ecx, (kernel_file.length_sectors % buffer_size_bytes) / 4 - a32 rep movsd -finished_loading: - call memory_map - - call vesa - - mov si, init_fpu_msg - call printrm - call initialize.fpu - - mov si, init_sse_msg - call printrm - call initialize.sse - - mov si, init_pit_msg - call printrm - call initialize.pit - - mov si, init_pic_msg - call printrm - call initialize.pic - - mov si, startup_arch_msg - call printrm - - jmp startup_arch - -%include "config.asm" -%include "descriptor_flags.inc" -%include "gdt_entry.inc" -%include "unreal.asm" -%include "memory_map.asm" -%include "vesa.asm" -%include "initialize.asm" - -init_fpu_msg: db "Init FPU",13,10,0 -init_sse_msg: db "Init SSE",13,10,0 -init_pit_msg: db "Init PIT",13,10,0 -init_pic_msg: db "Init PIC",13,10,0 -startup_arch_msg: db "Startup Arch",13,10,0 diff --git a/bootloader/x86_64/startup-i386.asm b/bootloader/x86_64/startup-i386.asm deleted file mode 100644 index c38a09e..0000000 --- a/bootloader/x86_64/startup-i386.asm +++ /dev/null @@ -1,148 +0,0 @@ -%include "startup-common.asm" - -startup_arch: - ; load protected mode GDT and IDT - cli - lgdt [gdtr] - lidt [idtr] - ; set protected mode bit of cr0 - mov eax, cr0 - or eax, 1 - mov cr0, eax - - ; far jump to load CS with 32 bit segment - jmp gdt.kernel_code:protected_mode - -USE32 -protected_mode: - ; load all the other segments with 32 bit data segments - mov eax, gdt.kernel_data - mov ds, eax - mov es, eax - mov fs, eax - mov gs, eax - mov ss, eax - - mov esp, 0x800000 - 128 - - mov eax, gdt.tss - ltr ax - - ;rust init - mov eax, [kernel_base + 0x18] - mov [interrupts.handler], eax - mov eax, gdtr - mov ebx, idtr - mov ecx, tss - int 255 -.lp: - sti - hlt - jmp .lp - -gdtr: - dw gdt.end + 1 ; size - dd gdt ; offset - -gdt: -.null equ $ - gdt - dq 0 - -.kernel_code equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code | attrib.readable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0 - iend - -.kernel_data equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0 - iend - -.user_code equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code | attrib.readable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0 - iend - -.user_data equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0 - iend - -.user_tls equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0 - iend - -.tss equ $ - gdt - istruc GDTEntry - at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF - at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF - at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF - at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe32 - at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF - at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF - iend -.end equ $ - gdt - -struc TSS - .prev_tss resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list. - .esp0 resd 1 ;The stack pointer to load when we change to kernel mode. - .ss0 resd 1 ;The stack segment to load when we change to kernel mode. - .esp1 resd 1 ;everything below here is unused now.. - .ss1 resd 1 - .esp2 resd 1 - .ss2 resd 1 - .cr3 resd 1 - .eip resd 1 - .eflags resd 1 - .eax resd 1 - .ecx resd 1 - .edx resd 1 - .ebx resd 1 - .esp resd 1 - .ebp resd 1 - .esi resd 1 - .edi resd 1 - .es resd 1 - .cs resd 1 - .ss resd 1 - .ds resd 1 - .fs resd 1 - .gs resd 1 - .ldt resd 1 - .trap resw 1 - .iomap_base resw 1 -endstruc - -tss: - istruc TSS - at TSS.esp0, dd 0x800000 - 128 - at TSS.ss0, dd gdt.kernel_data - at TSS.iomap_base, dw 0xFFFF - iend -.end: diff --git a/bootloader/x86_64/startup-x86_64.asm b/bootloader/x86_64/startup-x86_64.asm deleted file mode 100644 index 6059331..0000000 --- a/bootloader/x86_64/startup-x86_64.asm +++ /dev/null @@ -1,179 +0,0 @@ -trampoline: - .ready: dq 0 - .cpu_id: dq 0 - .page_table: dq 0 - .stack_start: dq 0 - .stack_end: dq 0 - .code: dq 0 - - times 512 - ($ - trampoline) db 0 - -startup_ap: - cli - - xor ax, ax - mov ds, ax - mov es, ax - mov ss, ax - - ; initialize stack - mov sp, 0x7C00 - - call initialize.fpu - call initialize.sse - - ;cr3 holds pointer to PML4 - mov edi, 0x70000 - mov cr3, edi - - ;enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension - mov eax, cr4 - or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4 - mov cr4, eax - - ; load protected mode GDT - lgdt [gdtr] - - mov ecx, 0xC0000080 ; Read from the EFER MSR. - rdmsr - or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit. - wrmsr - - ;enabling paging and protection simultaneously - mov ebx, cr0 - or ebx, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode - mov cr0, ebx - - ; far jump to enable Long Mode and load CS with 64 bit segment - jmp gdt.kernel_code:long_mode_ap - -%include "startup-common.asm" - -startup_arch: - cli - ; setting up Page Tables - ; Identity Mapping first GB - mov ax, 0x7000 - mov es, ax - - xor edi, edi - xor eax, eax - mov ecx, 6 * 4096 / 4 ;PML4, PDP, 4 PD / moves 4 Bytes at once - cld - rep stosd - - xor edi, edi - ;Link first PML4 and second to last PML4 to PDP - mov DWORD [es:edi], 0x71000 | 1 << 1 | 1 - mov DWORD [es:edi + 510*8], 0x71000 | 1 << 1 | 1 - add edi, 0x1000 - ;Link last PML4 to PML4 - mov DWORD [es:edi - 8], 0x70000 | 1 << 1 | 1 - ;Link first four PDP to PD - mov DWORD [es:edi], 0x72000 | 1 << 1 | 1 - mov DWORD [es:edi + 8], 0x73000 | 1 << 1 | 1 - mov DWORD [es:edi + 16], 0x74000 | 1 << 1 | 1 - mov DWORD [es:edi + 24], 0x75000 | 1 << 1 | 1 - add edi, 0x1000 - ;Link all PD's (512 per PDP, 2MB each)y - mov ebx, 1 << 7 | 1 << 1 | 1 - mov ecx, 4*512 -.setpd: - mov [es:edi], ebx - add ebx, 0x200000 - add edi, 8 - loop .setpd - - xor ax, ax - mov es, ax - - ;cr3 holds pointer to PML4 - mov edi, 0x70000 - mov cr3, edi - - ;enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension - mov eax, cr4 - or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4 - mov cr4, eax - - ; load protected mode GDT - lgdt [gdtr] - - mov ecx, 0xC0000080 ; Read from the EFER MSR. - rdmsr - or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit. - wrmsr - - ;enabling paging and protection simultaneously - mov ebx, cr0 - or ebx, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode - mov cr0, ebx - - ; far jump to enable Long Mode and load CS with 64 bit segment - jmp gdt.kernel_code:long_mode - -USE64 -long_mode: - ; load all the other segments with 64 bit data segments - mov rax, gdt.kernel_data - mov ds, rax - mov es, rax - mov fs, rax - mov gs, rax - mov ss, rax - - mov rsp, 0xFFFFFF000009F000 - - ;rust init - mov rax, [kernel_base + 0x18] - jmp rax - -long_mode_ap: - mov rax, gdt.kernel_data - mov ds, rax - mov es, rax - mov fs, rax - mov gs, rax - mov ss, rax - - mov rdi, [trampoline.cpu_id] - mov rsi, [trampoline.page_table] - mov rdx, [trampoline.stack_start] - mov rcx, [trampoline.stack_end] - - lea rsp, [rcx - 256] - - mov rax, [trampoline.code] - mov qword [trampoline.ready], 1 - jmp rax - -gdtr: - dw gdt.end + 1 ; size - dq gdt ; offset - -gdt: -.null equ $ - gdt - dq 0 - -.kernel_code equ $ - gdt -istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code - at GDTEntry.flags__limith, db flags.long_mode - at GDTEntry.baseh, db 0 -iend - -.kernel_data equ $ - gdt -istruc GDTEntry - at GDTEntry.limitl, dw 0 - at GDTEntry.basel, dw 0 - at GDTEntry.basem, db 0 -; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0 - at GDTEntry.baseh, db 0 -iend - -.end equ $ - gdt diff --git a/bootloader/x86_64/unreal.asm b/bootloader/x86_64/unreal.asm deleted file mode 100644 index 691a892..0000000 --- a/bootloader/x86_64/unreal.asm +++ /dev/null @@ -1,54 +0,0 @@ -SECTION .text -USE16 - -; switch to unreal mode; ds and es can address up to 4GiB -unreal: - cli - - lgdt [unreal_gdtr] - - push es - push ds - - mov eax, cr0 ; switch to pmode by - or al,1 ; set pmode bit - mov cr0, eax - - jmp $+2 - -; http://wiki.osdev.org/Babystep7 -; When this register given a "selector", a "segment descriptor cache register" -; is filled with the descriptor values, including the size (or limit). After -; the switch back to real mode, these values are not modified, regardless of -; what value is in the 16-bit segment register. So the 64k limit is no longer -; valid and 32-bit offsets can be used with the real-mode addressing rules - mov bx, unreal_gdt.data - mov es, bx - mov ds, bx - - and al,0xFE ; back to realmode - mov cr0, eax ; by toggling bit again - - pop ds - pop es - sti - ret - - -unreal_gdtr: - dw unreal_gdt.end + 1 ; size - dd unreal_gdt ; offset - -unreal_gdt: -.null equ $ - unreal_gdt - dq 0 -.data equ $ - unreal_gdt - istruc GDTEntry - at GDTEntry.limitl, dw 0xFFFF - at GDTEntry.basel, dw 0x0 - at GDTEntry.basem, db 0x0 - at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable - at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size - at GDTEntry.baseh, db 0x0 - iend -.end equ $ - unreal_gdt diff --git a/bootloader/x86_64/vesa.asm b/bootloader/x86_64/vesa.asm deleted file mode 100644 index b649e4b..0000000 --- a/bootloader/x86_64/vesa.asm +++ /dev/null @@ -1,207 +0,0 @@ -%include "vesa.inc" -SECTION .text -USE16 -vesa: -.getcardinfo: - mov ax, 0x4F00 - mov di, VBECardInfo - int 0x10 - cmp ax, 0x4F - je .findmode - mov eax, 1 - ret - .resetlist: - ;if needed, reset mins/maxes/stuff - xor cx, cx - mov [.minx], cx - mov [.miny], cx - mov [config.xres], cx - mov [config.yres], cx -.findmode: - mov si, [VBECardInfo.videomodeptr] - mov ax, [VBECardInfo.videomodeptr+2] - mov fs, ax - sub si, 2 -.searchmodes: - add si, 2 - mov cx, [fs:si] - cmp cx, 0xFFFF - jne .getmodeinfo - cmp word [.goodmode], 0 - je .resetlist - jmp .findmode -.getmodeinfo: - push esi - mov [.currentmode], cx - mov ax, 0x4F01 - mov di, VBEModeInfo - int 0x10 - pop esi - cmp ax, 0x4F - je .foundmode - mov eax, 1 - ret -.foundmode: - ;check minimum values, really not minimums from an OS perspective but ugly for users - cmp byte [VBEModeInfo.bitsperpixel], 32 - jb .searchmodes -.testx: - mov cx, [VBEModeInfo.xresolution] - cmp word [config.xres], 0 - je .notrequiredx - cmp cx, [config.xres] - je .testy - jmp .searchmodes -.notrequiredx: - cmp cx, [.minx] - jb .searchmodes -.testy: - mov cx, [VBEModeInfo.yresolution] - cmp word [config.yres], 0 - je .notrequiredy - cmp cx, [config.yres] - jne .searchmodes ;as if there weren't enough warnings, USE WITH CAUTION - cmp word [config.xres], 0 - jnz .setmode - jmp .testgood -.notrequiredy: - cmp cx, [.miny] - jb .searchmodes -.testgood: - mov cx, [.currentmode] - mov [.goodmode], cx - push esi - ; call decshowrm - ; mov al, ':' - ; call charrm - mov cx, [VBEModeInfo.xresolution] - call decshowrm - mov al, 'x' - call charrm - mov cx, [VBEModeInfo.yresolution] - call decshowrm - mov al, '@' - call charrm - xor ch, ch - mov cl, [VBEModeInfo.bitsperpixel] - call decshowrm - mov si, .modeok - call printrm - xor ax, ax - int 0x16 - pop esi - cmp al, 'y' - je .setmode - cmp al, 's' - je .savemode - jmp .searchmodes -.savemode: - mov cx, [VBEModeInfo.xresolution] - mov [config.xres], cx - mov cx, [VBEModeInfo.yresolution] - mov [config.yres], cx - call save_config -.setmode: - mov bx, [.currentmode] - cmp bx, 0 - je .nomode - or bx, 0x4000 - mov ax, 0x4F02 - int 0x10 -.nomode: - cmp ax, 0x4F - je .returngood - mov eax, 1 - ret -.returngood: - xor eax, eax - ret - -.minx dw 640 -.miny dw 480 - -.modeok db ": Is this OK? (s)ave/(y)es/(n)o",10,13,0 - -.goodmode dw 0 -.currentmode dw 0 -;useful functions - -decshowrm: - mov si, .number -.clear: - mov al, "0" - mov [si], al - inc si - cmp si, .numberend - jb .clear - dec si - call convertrm - mov si, .number -.lp: - lodsb - cmp si, .numberend - jae .end - cmp al, "0" - jbe .lp -.end: - dec si - call printrm - ret - -.number times 7 db 0 -.numberend db 0 - -convertrm: - dec si - mov bx, si ;place to convert into must be in si, number to convert must be in cx -.cnvrt: - mov si, bx - sub si, 4 -.ten4: inc si - cmp cx, 10000 - jb .ten3 - sub cx, 10000 - inc byte [si] - jmp .cnvrt -.ten3: inc si - cmp cx, 1000 - jb .ten2 - sub cx, 1000 - inc byte [si] - jmp .cnvrt -.ten2: inc si - cmp cx, 100 - jb .ten1 - sub cx, 100 - inc byte [si] - jmp .cnvrt -.ten1: inc si - cmp cx, 10 - jb .ten0 - sub cx, 10 - inc byte [si] - jmp .cnvrt -.ten0: inc si - cmp cx, 1 - jb .return - sub cx, 1 - inc byte [si] - jmp .cnvrt -.return: - ret - -printrm: - mov al, [si] - test al, al - jz .return - call charrm - inc si - jmp printrm -.return: - ret - -charrm: ;char must be in al - mov bx, 7 - mov ah, 0xE - int 10h - ret diff --git a/bootloader/x86_64/vesa.inc b/bootloader/x86_64/vesa.inc deleted file mode 100644 index 7f85476..0000000 --- a/bootloader/x86_64/vesa.inc +++ /dev/null @@ -1,90 +0,0 @@ -ABSOLUTE 0x5000 -VBECardInfo: - .signature resb 4 - .version resw 1 - .oemstring resd 1 - .capabilities resd 1 - .videomodeptr resd 1 - .totalmemory resw 1 - .oemsoftwarerev resw 1 - .oemvendornameptr resd 1 - .oemproductnameptr resd 1 - .oemproductrevptr resd 1 - .reserved resb 222 - .oemdata resb 256 - -ABSOLUTE 0x5200 -VBEModeInfo: - .attributes resw 1 - .winA resb 1 - .winB resb 1 - .granularity resw 1 - .winsize resw 1 - .segmentA resw 1 - .segmentB resw 1 - .winfuncptr resd 1 - .bytesperscanline resw 1 - .xresolution resw 1 - .yresolution resw 1 - .xcharsize resb 1 - .ycharsize resb 1 - .numberofplanes resb 1 - .bitsperpixel resb 1 - .numberofbanks resb 1 - .memorymodel resb 1 - .banksize resb 1 - .numberofimagepages resb 1 - .unused resb 1 - .redmasksize resb 1 - .redfieldposition resb 1 - .greenmasksize resb 1 - .greenfieldposition resb 1 - .bluemasksize resb 1 - .bluefieldposition resb 1 - .rsvdmasksize resb 1 - .rsvdfieldposition resb 1 - .directcolormodeinfo resb 1 - .physbaseptr resd 1 - .offscreenmemoryoffset resd 1 - .offscreenmemsize resw 1 - .reserved resb 206 - -VBE.ModeAttributes: - .available equ 1 << 0 - .bios equ 1 << 2 - .color equ 1 << 3 - .graphics equ 1 << 4 - .vgacompatible equ 1 << 5 - .notbankable equ 1 << 6 - .linearframebuffer equ 1 << 7 - -ABSOLUTE 0x5400 -VBEEDID: - .header resb 8 - .manufacturer resw 1 - .productid resw 1 - .serial resd 1 - .manufactureweek resb 1 - .manufactureyear resb 1 - .version resb 1 - .revision resb 1 - .input resb 1 - .horizontalsize resb 1 - .verticalsize resb 1 - .gamma resb 1 - .displaytype resb 1 - .chromaticity resb 10 - .timingI resb 1 - .timingII resb 1 - .timingreserved resb 1 - .standardtiming: resw 8 ;format: db (horizontal-248)/8, aspectratio | verticalfrequency - 60 - .aspect.16.10 equ 0 ;mul horizontal by 10, shr 4 to get vertical resolution - .aspect.4.3 equ 1 << 6 ;mul horizontal by 3, shr 2 to get vertical resolution - .aspect.5.4 equ 2 << 6 ;shl horizontal by 2, div by 5 to get vertical resolution - .aspect.16.9 equ 3 << 6 ;mul horizontal by 9, shr by 4 to get vertical resolution - .descriptorblock1 resb 18 - .descriptorblock2 resb 18 - .descriptorblock3 resb 18 - .descriptorblock4 resb 18 - .extensionflag resb 1 - .checksum resb 1 \ No newline at end of file From df80a0bc56c25664cc1e97b53360b61315e6569d Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 5 Jan 2017 11:28:34 -0700 Subject: [PATCH 6/6] Move isolinux to submodule --- .gitmodules | 3 + isolinux | 1 + isolinux/COPYING | 340 ------------------------------------------ isolinux/isolinux.bin | Bin 40960 -> 0 bytes isolinux/isolinux.cfg | 6 - isolinux/ldlinux.c32 | Bin 116492 -> 0 bytes isolinux/memdisk | Bin 25628 -> 0 bytes 7 files changed, 4 insertions(+), 346 deletions(-) create mode 160000 isolinux delete mode 100644 isolinux/COPYING delete mode 100644 isolinux/isolinux.bin delete mode 100644 isolinux/isolinux.cfg delete mode 100644 isolinux/ldlinux.c32 delete mode 100644 isolinux/memdisk diff --git a/.gitmodules b/.gitmodules index 98d2765..f706dda2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -67,3 +67,6 @@ [submodule "bootloader"] path = bootloader url = https://github.com/redox-os/bootloader.git +[submodule "isolinux"] + path = isolinux + url = https://github.com/redox-os/isolinux.git diff --git a/isolinux b/isolinux new file mode 160000 index 0000000..3cf79d3 --- /dev/null +++ b/isolinux @@ -0,0 +1 @@ +Subproject commit 3cf79d335400af8fc3a87a13f0ae12777a766b3b diff --git a/isolinux/COPYING b/isolinux/COPYING deleted file mode 100644 index 60549be..0000000 --- a/isolinux/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/isolinux/isolinux.bin b/isolinux/isolinux.bin deleted file mode 100644 index e099c90fa014ba2aef5d5f032ef42ae441282c73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 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 diff --git a/isolinux/isolinux.cfg b/isolinux/isolinux.cfg deleted file mode 100644 index 20e6848..0000000 --- a/isolinux/isolinux.cfg +++ /dev/null @@ -1,6 +0,0 @@ -prompt 0 -default 1 - -label 1 - kernel /isolinux/memdisk - append initrd=/livedisk.gz diff --git a/isolinux/ldlinux.c32 b/isolinux/ldlinux.c32 deleted file mode 100644 index e4330130d0d4debabd6517b70de8492bc09159d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116492 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

*`v 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