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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

B5T#Xf9$tvoyc6`w% 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