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;