From dad81d3c46414bcb0abefc236860b756f3a7839c Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 26 Sep 2016 17:00:06 -0600 Subject: [PATCH] WIP: AHCI drivers and more memory syscalls --- .../x86_64/src/memory/area_frame_allocator.rs | 18 +- arch/x86_64/src/memory/mod.rs | 22 +- drivers/ahcid/Cargo.toml | 9 + drivers/ahcid/src/ahci/fis.rs | 155 +++++++ drivers/ahcid/src/ahci/hba.rs | 393 ++++++++++++++++++ drivers/ahcid/src/ahci/mod.rs | 83 ++++ drivers/ahcid/src/main.rs | 35 ++ drivers/pcid/src/main.rs | 22 +- initfs/etc/pcid.toml | 2 +- kernel/scheme/debug.rs | 2 +- kernel/syscall/mod.rs | 3 + kernel/syscall/process.rs | 20 +- syscall/src/lib.rs | 12 + syscall/src/number.rs | 7 +- 14 files changed, 757 insertions(+), 26 deletions(-) create mode 100644 drivers/ahcid/Cargo.toml create mode 100644 drivers/ahcid/src/ahci/fis.rs create mode 100644 drivers/ahcid/src/ahci/hba.rs create mode 100644 drivers/ahcid/src/ahci/mod.rs create mode 100644 drivers/ahcid/src/main.rs diff --git a/arch/x86_64/src/memory/area_frame_allocator.rs b/arch/x86_64/src/memory/area_frame_allocator.rs index 15f21a2..54088d2 100644 --- a/arch/x86_64/src/memory/area_frame_allocator.rs +++ b/arch/x86_64/src/memory/area_frame_allocator.rs @@ -43,11 +43,12 @@ impl AreaFrameAllocator { } impl FrameAllocator for AreaFrameAllocator { - fn allocate_frame(&mut self) -> Option { + fn allocate_frames(&mut self, count: usize) -> Option { if let Some(area) = self.current_area { // "Clone" the frame to return it if it's free. Frame doesn't // implement Clone, but we can construct an identical frame. - let frame = Frame{ number: self.next_free_frame.number }; + let start_frame = Frame{ number: self.next_free_frame.number }; + let end_frame = Frame { number: self.next_free_frame.number + count }; // the last frame of the current area let current_area_last_frame = { @@ -55,27 +56,28 @@ impl FrameAllocator for AreaFrameAllocator { Frame::containing_address(PhysicalAddress::new(address as usize)) }; - if frame > current_area_last_frame { + if end_frame > current_area_last_frame { // all frames of current area are used, switch to next area self.choose_next_area(); - } else if frame >= self.kernel_start && frame <= self.kernel_end { + } else if (start_frame >= self.kernel_start && start_frame <= self.kernel_end) + || (end_frame >= self.kernel_start && end_frame <= self.kernel_end) { // `frame` is used by the kernel self.next_free_frame = Frame { number: self.kernel_end.number + 1 }; } else { // frame is unused, increment `next_free_frame` and return it - self.next_free_frame.number += 1; - return Some(frame); + self.next_free_frame.number += count; + return Some(start_frame); } // `frame` was not valid, try it again with the updated `next_free_frame` - self.allocate_frame() + self.allocate_frames(count) } else { None // no free frames left } } - fn deallocate_frame(&mut self, frame: Frame) { + fn deallocate_frames(&mut self, frame: Frame, count: usize) { //panic!("AreaFrameAllocator::deallocate_frame: not supported: {:?}", frame); } } diff --git a/arch/x86_64/src/memory/mod.rs b/arch/x86_64/src/memory/mod.rs index 02c6c2b..f7f5048 100644 --- a/arch/x86_64/src/memory/mod.rs +++ b/arch/x86_64/src/memory/mod.rs @@ -69,17 +69,27 @@ pub unsafe fn init(kernel_start: usize, kernel_end: usize) { /// Allocate a frame pub fn allocate_frame() -> Option { + allocate_frames(1) +} + +/// Deallocate a frame +pub fn deallocate_frame(frame: Frame) { + deallocate_frames(frame, 1) +} + +/// Allocate a range of frames +pub fn allocate_frames(count: usize) -> Option { if let Some(ref mut allocator) = *ALLOCATOR.lock() { - allocator.allocate_frame() + allocator.allocate_frames(count) } else { panic!("frame allocator not initialized"); } } -/// Deallocate a frame -pub fn deallocate_frame(frame: Frame) { +/// Deallocate a range of frames frame +pub fn deallocate_frames(frame: Frame, count: usize) { if let Some(ref mut allocator) = *ALLOCATOR.lock() { - allocator.deallocate_frame(frame) + allocator.deallocate_frames(frame, count) } else { panic!("frame allocator not initialized"); } @@ -151,6 +161,6 @@ impl Iterator for FrameIter { } pub trait FrameAllocator { - fn allocate_frame(&mut self) -> Option; - fn deallocate_frame(&mut self, frame: Frame); + fn allocate_frames(&mut self, size: usize) -> Option; + fn deallocate_frames(&mut self, frame: Frame, size: usize); } diff --git a/drivers/ahcid/Cargo.toml b/drivers/ahcid/Cargo.toml new file mode 100644 index 0000000..2bc8fa8 --- /dev/null +++ b/drivers/ahcid/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ahcid" +version = "0.1.0" + +[dependencies] +bitflags = "*" +io = { path = "../io/" } +spin = "*" +syscall = { path = "../../syscall/" } diff --git a/drivers/ahcid/src/ahci/fis.rs b/drivers/ahcid/src/ahci/fis.rs new file mode 100644 index 0000000..7dbe33c --- /dev/null +++ b/drivers/ahcid/src/ahci/fis.rs @@ -0,0 +1,155 @@ +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 new file mode 100644 index 0000000..f7ca720 --- /dev/null +++ b/drivers/ahcid/src/ahci/hba.rs @@ -0,0 +1,393 @@ +use io::{Io, Mmio}; + +use std::mem::size_of; +use std::{ptr, u32}; + +use syscall::{physalloc, physmap, physunmap, virttophys, MAP_WRITE}; +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_TFES: u32 = 1 << 30; +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; + +#[derive(Debug)] +pub enum HbaPortType { + None, + Unknown(u32), + SATA, + SATAPI, + PM, + SEMB, +} + +#[repr(packed)] +pub struct HbaPort { + pub clb: Mmio, // 0x00, command list base address, 1K-byte aligned + pub fb: Mmio, // 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 init(&mut self) { + self.stop(); + + let clb_phys = unsafe { physalloc(size_of::()).unwrap() }; + self.clb.write(clb_phys as u64); + + let fb = unsafe { physalloc(256).unwrap() }; + self.fb.write(fb as u64); + + let clb = unsafe { physmap(clb_phys, size_of::(), MAP_WRITE).unwrap() } as *mut HbaCmdHeader; + for i in 0..32 { + let cmdheader = unsafe { &mut *clb.offset(i) }; + let ctba = unsafe { physalloc(size_of::()).unwrap() }; + cmdheader.ctba.write(ctba as u64); + cmdheader.prdtl.write(0); + } + unsafe { physunmap(clb as usize).unwrap(); } + + self.start(); + } + + pub unsafe fn identify(&mut self, port: usize) -> Option { + self.is.write(u32::MAX); + + let dest_phys = physalloc(256).unwrap(); + let dest = physmap(dest_phys, 256, MAP_WRITE).unwrap() as *mut u16; + + if let Some(slot) = self.slot() { + { + let clb = unsafe { physmap(self.clb.read() as usize, size_of::(), MAP_WRITE).unwrap() } as *mut HbaCmdHeader; + let cmdheader = &mut *clb.offset(slot as isize); + cmdheader.cfl.write(((size_of::() / size_of::()) as u8)); + cmdheader.prdtl.write(1); + + let ctba = unsafe { physmap(cmdheader.ctba.read() as usize, size_of::(), MAP_WRITE).unwrap() } as *mut HbaCmdTable; + ptr::write_bytes(ctba as *mut u8, 0, size_of::()); + let cmdtbl = &mut *(ctba); + + let prdt_entry = &mut cmdtbl.prdt_entry[0]; + prdt_entry.dba.write(dest_phys as u64); + prdt_entry.dbc.write(512 | 1); + + let cmdfis = &mut *(cmdtbl.cfis.as_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); + + unsafe { physunmap(ctba as usize).unwrap(); } + unsafe { physunmap(clb as usize).unwrap(); } + } + + while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {} + + self.ci.writef(1 << slot, true); + + while self.ci.readf(1 << slot) { + if self.is.readf(HBA_PORT_IS_TFES) { + return None; + } + } + + if self.is.readf(HBA_PORT_IS_TFES) { + return None; + } + + let mut serial = String::new(); + for word in 10..20 { + let d = *dest.offset(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.offset(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.offset(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.offset(100) as u64) | + ((*dest.offset(101) as u64) << 16) | + ((*dest.offset(102) as u64) << 32) | + ((*dest.offset(103) as u64) << 48); + + let lba_bits = if sectors == 0 { + sectors = (*dest.offset(60) as u64) | ((*dest.offset(61) as u64) << 16); + 28 + } else { + 48 + }; + + println!(" + Port {}: Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB", + port, serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048); + + Some(sectors * 512) + } else { + None + } + } + + pub fn start(&mut self) { + while self.cmd.readf(HBA_PORT_CMD_CR) {} + + self.cmd.writef(HBA_PORT_CMD_FRE, true); + self.cmd.writef(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) {} + + 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 ata_dma_small(&mut self, block: u64, sectors: usize, mut buf: usize, write: bool) -> Result { + if buf >= 0x80000000 { + buf -= 0x80000000; + } + + // TODO: PRDTL for files larger than 4MB + let entries = 1; + + if buf > 0 && sectors > 0 { + self.is.write(u32::MAX); + + if let Some(slot) = self.slot() { + println!("Slot {}", slot); + + let clb = self.clb.read() as usize; + let cmdheader = unsafe { &mut *(clb as *mut HbaCmdHeader).offset(slot as isize) }; + + cmdheader.cfl.write(((size_of::() / size_of::()) as u8)); + cmdheader.cfl.writef(1 << 6, write); + + cmdheader.prdtl.write(entries); + + let ctba = cmdheader.ctba.read() as usize; + unsafe { ptr::write_bytes(ctba as *mut u8, 0, size_of::()) }; + let cmdtbl = unsafe { &mut *(ctba as *mut HbaCmdTable) }; + + let prdt_entry = &mut cmdtbl.prdt_entry[0]; + prdt_entry.dba.write(buf as u64); + prdt_entry.dbc.write(((sectors * 512) as u32) | 1); + + let cmdfis = unsafe { &mut *(cmdtbl.cfis.as_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); + + println!("Busy Wait"); + while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {} + + self.ci.writef(1 << slot, true); + + println!("Completion Wait"); + while self.ci.readf(1 << slot) { + if self.is.readf(HBA_PORT_IS_TFES) { + return Err(Error::new(EIO)); + } + } + + if self.is.readf(HBA_PORT_IS_TFES) { + return Err(Error::new(EIO)); + } + + Ok(sectors * 512) + } else { + println!("No Command Slots"); + Err(Error::new(EIO)) + } + } else { + println!("Invalid request"); + Err(Error::new(EIO)) + } + } + + pub fn ata_dma(&mut self, block: u64, sectors: usize, buf: usize, write: bool) -> Result { + println!("AHCI {:X} DMA BLOCK: {:X} SECTORS: {} BUF: {:X} WRITE: {}", (self as *mut HbaPort) as usize, block, sectors, buf, write); + + if sectors > 0 { + let physical_address = try!(unsafe { virttophys(buf) }); + + let mut sector: usize = 0; + while sectors - sector >= 255 { + if let Err(err) = self.ata_dma_small(block + sector as u64, 255, physical_address + sector * 512, write) { + return Err(err); + } + + sector += 255; + } + if sector < sectors { + if let Err(err) = self.ata_dma_small(block + sector as u64, sectors - sector, physical_address + sector * 512, write) { + return Err(err); + } + } + + Ok(sectors * 512) + } else { + println!("Invalid request"); + 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 +} + +#[repr(packed)] +struct HbaPrdtEntry { + dba: Mmio, // Data base address + rsv0: Mmio, // Reserved + dbc: Mmio, // Byte count, 4M max, interrupt = 1 +} + +#[repr(packed)] +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)] +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 new file mode 100644 index 0000000..cdf54ca --- /dev/null +++ b/drivers/ahcid/src/ahci/mod.rs @@ -0,0 +1,83 @@ +use io::Io; + +use syscall::error::Result; + +use self::hba::{HbaMem, HbaPort, HbaPortType}; + +pub mod fis; +pub mod hba; + +pub struct Ahci; + +impl Ahci { + pub fn disks(base: usize, irq: u8) -> Vec { + println!(" + AHCI on: {:X} IRQ: {:X}", base as usize, irq); + + 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 mut disk = AhciDisk::new(base, i, irq); + let port_type = disk.port.probe(); + println!("{}: {:?}", i, port_type); + match port_type { + HbaPortType::SATA => { + /* + disk.port.init(); + if let Some(size) = unsafe { disk.port.identify(i) } { + disk.size = size; + Some(disk) + } else { + None + } + */ + None + } + _ => None, + } + }) + .collect(); + + ret + } +} + +pub struct AhciDisk { + port: &'static mut HbaPort, + port_index: usize, + irq: u8, + size: u64, +} + +impl AhciDisk { + fn new(base: usize, port_index: usize, irq: u8) -> Self { + AhciDisk { + port: &mut unsafe { &mut *(base as *mut HbaMem) }.ports[port_index], + port_index: port_index, + irq: irq, + size: 0 + } + } + + fn name(&self) -> String { + format!("AHCI Port {}", self.port_index) + } + + fn on_irq(&mut self, irq: u8) { + if irq == self.irq { + //debugln!("AHCI IRQ"); + } + } + + fn size(&self) -> u64 { + self.size + } + + fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result { + self.port.ata_dma(block, buffer.len() / 512, buffer.as_ptr() as usize, false) + } + + fn write(&mut self, block: u64, buffer: &[u8]) -> Result { + self.port.ata_dma(block, buffer.len() / 512, buffer.as_ptr() as usize, true) + } +} diff --git a/drivers/ahcid/src/main.rs b/drivers/ahcid/src/main.rs new file mode 100644 index 0000000..5f7b1a1 --- /dev/null +++ b/drivers/ahcid/src/main.rs @@ -0,0 +1,35 @@ +#![feature(asm)] + +#[macro_use] +extern crate bitflags; +extern crate io; +extern crate syscall; + +use std::{env, thread, usize}; + +use syscall::{iopl, physmap, physunmap, MAP_WRITE}; + +pub mod ahci; + +fn main() { + let mut args = env::args().skip(1); + + 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"); + + thread::spawn(move || { + unsafe { + iopl(3).expect("ahcid: failed to get I/O permission"); + asm!("cli" :::: "intel", "volatile"); + } + + let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") }; + { + ahci::Ahci::disks(address, irq); + } + unsafe { physunmap(address).expect("ahcid: failed to unmap address") }; + }); +} diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs index 9eb0889..a34ff17 100644 --- a/drivers/pcid/src/main.rs +++ b/drivers/pcid/src/main.rs @@ -120,17 +120,25 @@ fn main() { } }; let arg = match arg.as_str() { - "$0" => bar_arg(0), - "$1" => bar_arg(1), - "$2" => bar_arg(2), - "$3" => bar_arg(3), - "$4" => bar_arg(4), - "$5" => bar_arg(5), + "$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!("{:?}", 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/initfs/etc/pcid.toml b/initfs/etc/pcid.toml index ec6c838..996b9a8 100644 --- a/initfs/etc/pcid.toml +++ b/initfs/etc/pcid.toml @@ -2,4 +2,4 @@ name = "AHCI storage" class = 1 subclass = 6 -command = ["ahcid", "$5"] +command = ["initfs:bin/ahcid", "$BAR5", "$IRQ"] diff --git a/kernel/scheme/debug.rs b/kernel/scheme/debug.rs index 52b88e5..eed44ed 100644 --- a/kernel/scheme/debug.rs +++ b/kernel/scheme/debug.rs @@ -72,7 +72,7 @@ impl Scheme for DebugScheme { Ok(buffer.len()) } - fn fevent(&self, _file: usize, flags: usize) -> Result { + fn fevent(&self, _file: usize, _flags: usize) -> Result { Ok(0) } diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs index 1bb5a56..2815091 100644 --- a/kernel/syscall/mod.rs +++ b/kernel/syscall/mod.rs @@ -47,8 +47,11 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?), SYS_FEVENT => fevent(b, c), SYS_FPATH => fpath(b, validate_slice_mut(c as *mut u8, d)?), + SYS_PHYSALLOC => physalloc(b), + SYS_PHYSFREE => physfree(b, c), SYS_PHYSMAP => physmap(b, c, d), SYS_PHYSUNMAP => physunmap(b), + SYS_VIRTTOPHYS => virttophys(b), _ => { println!("Unknown syscall {}", a); Err(Error::new(ENOSYS)) diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index 5150b95..d953e42 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -8,7 +8,7 @@ use spin::Mutex; use arch; use arch::externs::memcpy; -use arch::memory::allocate_frame; +use arch::memory::{allocate_frame, allocate_frames, deallocate_frames, Frame}; use arch::paging::{ActivePageTable, InactivePageTable, Page, PhysicalAddress, VirtualAddress, entry}; use arch::paging::temporary_page::TemporaryPage; use arch::start::usermode; @@ -535,6 +535,16 @@ pub fn iopl(_level: usize) -> Result { Ok(0) } +pub fn physalloc(size: usize) -> Result { + allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get()) +} + +pub fn physfree(physical_address: usize, size: usize) -> Result { + deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), (size + 4095)/4096); + //TODO: Check that no double free occured + Ok(0) +} + //TODO: verify exlusive access to physical memory pub fn physmap(physical_address: usize, size: usize, flags: usize) -> Result { if size == 0 { @@ -617,6 +627,14 @@ pub fn sched_yield() -> Result { Ok(0) } +pub fn virttophys(virtual_address: usize) -> Result { + let active_table = unsafe { ActivePageTable::new() }; + match active_table.translate(VirtualAddress::new(virtual_address)) { + Some(physical_address) => Ok(physical_address.get()), + None => Err(Error::new(EFAULT)) + } +} + pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result { loop { { diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 2f4ea89..15ac101 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -114,6 +114,14 @@ pub fn open(path: &str, flags: usize) -> Result { unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) } } +pub unsafe fn physalloc(size: usize) -> Result { + syscall1(SYS_PHYSALLOC, size) +} + +pub unsafe fn physfree(physical_address: usize, size: usize) -> Result { + syscall2(SYS_PHYSFREE, physical_address, size) +} + pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result { syscall3(SYS_PHYSMAP, physical_address, size, flags) } @@ -138,6 +146,10 @@ pub fn unlink(path: &str) -> Result { unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) } } +pub unsafe fn virttophys(virtual_address: usize) -> Result { + syscall1(SYS_VIRTTOPHYS, virtual_address) +} + pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result { unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) } } diff --git a/syscall/src/number.rs b/syscall/src/number.rs index c0f341c..b0d41c0 100644 --- a/syscall/src/number.rs +++ b/syscall/src/number.rs @@ -20,8 +20,11 @@ pub const SYS_LSEEK: usize = 19; pub const SYS_MKDIR: usize = 39; pub const SYS_NANOSLEEP: usize = 162; pub const SYS_OPEN: usize = 5; -pub const SYS_PHYSMAP: usize = 945; -pub const SYS_PHYSUNMAP: usize = 946; +pub const SYS_PHYSALLOC: usize = 945; +pub const SYS_PHYSFREE: usize = 946; +pub const SYS_PHYSMAP: usize = 947; +pub const SYS_PHYSUNMAP: usize = 948; +pub const SYS_VIRTTOPHYS: usize = 949; pub const SYS_PIPE2: usize = 331; pub const SYS_READ: usize = 3; pub const SYS_RMDIR: usize = 84;