WIP: AHCI drivers and more memory syscalls
This commit is contained in:
parent
98399b030f
commit
dad81d3c46
|
@ -43,11 +43,12 @@ impl AreaFrameAllocator {
|
|||
}
|
||||
|
||||
impl FrameAllocator for AreaFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame> {
|
||||
fn allocate_frames(&mut self, count: usize) -> Option<Frame> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,17 +69,27 @@ pub unsafe fn init(kernel_start: usize, kernel_end: usize) {
|
|||
|
||||
/// Allocate a frame
|
||||
pub fn allocate_frame() -> Option<Frame> {
|
||||
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<Frame> {
|
||||
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<Frame>;
|
||||
fn deallocate_frame(&mut self, frame: Frame);
|
||||
fn allocate_frames(&mut self, size: usize) -> Option<Frame>;
|
||||
fn deallocate_frames(&mut self, frame: Frame, size: usize);
|
||||
}
|
||||
|
|
9
drivers/ahcid/Cargo.toml
Normal file
9
drivers/ahcid/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "ahcid"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "*"
|
||||
io = { path = "../io/" }
|
||||
spin = "*"
|
||||
syscall = { path = "../../syscall/" }
|
155
drivers/ahcid/src/ahci/fis.rs
Normal file
155
drivers/ahcid/src/ahci/fis.rs
Normal file
|
@ -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<u8>, // FIS_TYPE_REG_H2D
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, 1: Command, 0: Control
|
||||
|
||||
pub command: Mmio<u8>, // Command register
|
||||
pub featurel: Mmio<u8>, // Feature register, 7:0
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub featureh: Mmio<u8>, // Feature register, 15:8
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub icc: Mmio<u8>, // Isochronous command completion
|
||||
pub control: Mmio<u8>, // Control register
|
||||
|
||||
// DWORD 4
|
||||
pub rsv1: [Mmio<u8>; 4], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisRegD2H {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_REG_D2H
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, Interrupt bit: 2
|
||||
|
||||
pub status: Mmio<u8>, // Status register
|
||||
pub error: Mmio<u8>, // Error register
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub rsv2: Mmio<u8>, // Reserved
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub rsv3: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 4
|
||||
pub rsv4: [Mmio<u8>; 4], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisData {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_DATA
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier
|
||||
|
||||
pub rsv1: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 1 ~ N
|
||||
pub data: [Mmio<u8>; 252], // Payload
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisPioSetup {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_PIO_SETUP
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2
|
||||
|
||||
pub status: Mmio<u8>, // Status register
|
||||
pub error: Mmio<u8>, // Error register
|
||||
|
||||
// DWORD 1
|
||||
pub lba0: Mmio<u8>, // LBA low register, 7:0
|
||||
pub lba1: Mmio<u8>, // LBA mid register, 15:8
|
||||
pub lba2: Mmio<u8>, // LBA high register, 23:16
|
||||
pub device: Mmio<u8>, // Device register
|
||||
|
||||
// DWORD 2
|
||||
pub lba3: Mmio<u8>, // LBA register, 31:24
|
||||
pub lba4: Mmio<u8>, // LBA register, 39:32
|
||||
pub lba5: Mmio<u8>, // LBA register, 47:40
|
||||
pub rsv2: Mmio<u8>, // Reserved
|
||||
|
||||
// DWORD 3
|
||||
pub countl: Mmio<u8>, // Count register, 7:0
|
||||
pub counth: Mmio<u8>, // Count register, 15:8
|
||||
pub rsv3: Mmio<u8>, // Reserved
|
||||
pub e_status: Mmio<u8>, // New value of status register
|
||||
|
||||
// DWORD 4
|
||||
pub tc: Mmio<u16>, // Transfer count
|
||||
pub rsv4: [Mmio<u8>; 2], // Reserved
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct FisDmaSetup {
|
||||
// DWORD 0
|
||||
pub fis_type: Mmio<u8>, // FIS_TYPE_DMA_SETUP
|
||||
|
||||
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2, auto-activate: 1
|
||||
|
||||
pub rsv1: [Mmio<u8>; 2], // Reserved
|
||||
|
||||
// DWORD 1&2
|
||||
pub dma_buffer_id: Mmio<u64>, /* 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<u32>, // More reserved
|
||||
|
||||
// DWORD 4
|
||||
pub dma_buffer_offset: Mmio<u32>, // Byte offset into buffer. First 2 bits must be 0
|
||||
|
||||
// DWORD 5
|
||||
pub transfer_count: Mmio<u32>, // Number of bytes to transfer. Bit 0 must be 0
|
||||
|
||||
// DWORD 6
|
||||
pub rsv6: Mmio<u32>, // Reserved
|
||||
}
|
393
drivers/ahcid/src/ahci/hba.rs
Normal file
393
drivers/ahcid/src/ahci/hba.rs
Normal file
|
@ -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<u64>, // 0x00, command list base address, 1K-byte aligned
|
||||
pub fb: Mmio<u64>, // 0x08, FIS base address, 256-byte aligned
|
||||
pub is: Mmio<u32>, // 0x10, interrupt status
|
||||
pub ie: Mmio<u32>, // 0x14, interrupt enable
|
||||
pub cmd: Mmio<u32>, // 0x18, command and status
|
||||
pub rsv0: Mmio<u32>, // 0x1C, Reserved
|
||||
pub tfd: Mmio<u32>, // 0x20, task file data
|
||||
pub sig: Mmio<u32>, // 0x24, signature
|
||||
pub ssts: Mmio<u32>, // 0x28, SATA status (SCR0:SStatus)
|
||||
pub sctl: Mmio<u32>, // 0x2C, SATA control (SCR2:SControl)
|
||||
pub serr: Mmio<u32>, // 0x30, SATA error (SCR1:SError)
|
||||
pub sact: Mmio<u32>, // 0x34, SATA active (SCR3:SActive)
|
||||
pub ci: Mmio<u32>, // 0x38, command issue
|
||||
pub sntf: Mmio<u32>, // 0x3C, SATA notification (SCR4:SNotification)
|
||||
pub fbs: Mmio<u32>, // 0x40, FIS-based switch control
|
||||
pub rsv1: [Mmio<u32>; 11], // 0x44 ~ 0x6F, Reserved
|
||||
pub vendor: [Mmio<u32>; 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::<HbaCmdHeader>()).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::<HbaCmdHeader>(), MAP_WRITE).unwrap() } as *mut HbaCmdHeader;
|
||||
for i in 0..32 {
|
||||
let cmdheader = unsafe { &mut *clb.offset(i) };
|
||||
let ctba = unsafe { physalloc(size_of::<HbaCmdTable>()).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<u64> {
|
||||
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::<HbaCmdHeader>(), MAP_WRITE).unwrap() } as *mut HbaCmdHeader;
|
||||
let cmdheader = &mut *clb.offset(slot as isize);
|
||||
cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8));
|
||||
cmdheader.prdtl.write(1);
|
||||
|
||||
let ctba = unsafe { physmap(cmdheader.ctba.read() as usize, size_of::<HbaCmdTable>(), MAP_WRITE).unwrap() } as *mut HbaCmdTable;
|
||||
ptr::write_bytes(ctba as *mut u8, 0, size_of::<HbaCmdTable>());
|
||||
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<u32> {
|
||||
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<usize> {
|
||||
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::<FisRegH2D>() / size_of::<u32>()) 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::<HbaCmdTable>()) };
|
||||
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<usize> {
|
||||
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<u32>, // 0x00, Host capability
|
||||
pub ghc: Mmio<u32>, // 0x04, Global host control
|
||||
pub is: Mmio<u32>, // 0x08, Interrupt status
|
||||
pub pi: Mmio<u32>, // 0x0C, Port implemented
|
||||
pub vs: Mmio<u32>, // 0x10, Version
|
||||
pub ccc_ctl: Mmio<u32>, // 0x14, Command completion coalescing control
|
||||
pub ccc_pts: Mmio<u32>, // 0x18, Command completion coalescing ports
|
||||
pub em_loc: Mmio<u32>, // 0x1C, Enclosure management location
|
||||
pub em_ctl: Mmio<u32>, // 0x20, Enclosure management control
|
||||
pub cap2: Mmio<u32>, // 0x24, Host capabilities extended
|
||||
pub bohc: Mmio<u32>, // 0x28, BIOS/OS handoff control and status
|
||||
pub rsv: [Mmio<u8>; 116], // 0x2C - 0x9F, Reserved
|
||||
pub vendor: [Mmio<u8>; 96], // 0xA0 - 0xFF, Vendor specific registers
|
||||
pub ports: [HbaPort; 32], // 0x100 - 0x10FF, Port control registers
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct HbaPrdtEntry {
|
||||
dba: Mmio<u64>, // Data base address
|
||||
rsv0: Mmio<u32>, // Reserved
|
||||
dbc: Mmio<u32>, // Byte count, 4M max, interrupt = 1
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct HbaCmdTable {
|
||||
// 0x00
|
||||
cfis: [Mmio<u8>; 64], // Command FIS
|
||||
|
||||
// 0x40
|
||||
acmd: [Mmio<u8>; 16], // ATAPI command, 12 or 16 bytes
|
||||
|
||||
// 0x50
|
||||
rsv: [Mmio<u8>; 48], // Reserved
|
||||
|
||||
// 0x80
|
||||
prdt_entry: [HbaPrdtEntry; 65536], // Physical region descriptor table entries, 0 ~ 65535
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct HbaCmdHeader {
|
||||
// DW0
|
||||
cfl: Mmio<u8>, /* Command FIS length in DWORDS, 2 ~ 16, atapi: 4, write - host to device: 2, prefetchable: 1 */
|
||||
pm: Mmio<u8>, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier
|
||||
|
||||
prdtl: Mmio<u16>, // Physical region descriptor table length in entries
|
||||
|
||||
// DW1
|
||||
prdbc: Mmio<u32>, // Physical region descriptor byte count transferred
|
||||
|
||||
// DW2, 3
|
||||
ctba: Mmio<u64>, // Command table descriptor base address
|
||||
|
||||
// DW4 - 7
|
||||
rsv1: [Mmio<u32>; 4], // Reserved
|
||||
}
|
83
drivers/ahcid/src/ahci/mod.rs
Normal file
83
drivers/ahcid/src/ahci/mod.rs
Normal file
|
@ -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<AhciDisk> {
|
||||
println!(" + AHCI on: {:X} IRQ: {:X}", base as usize, irq);
|
||||
|
||||
let pi = unsafe { &mut *(base as *mut HbaMem) }.pi.read();
|
||||
let ret: Vec<AhciDisk> = (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<usize> {
|
||||
self.port.ata_dma(block, buffer.len() / 512, buffer.as_ptr() as usize, false)
|
||||
}
|
||||
|
||||
fn write(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||
self.port.ata_dma(block, buffer.len() / 512, buffer.as_ptr() as usize, true)
|
||||
}
|
||||
}
|
35
drivers/ahcid/src/main.rs
Normal file
35
drivers/ahcid/src/main.rs
Normal file
|
@ -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::<u8>().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") };
|
||||
});
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
name = "AHCI storage"
|
||||
class = 1
|
||||
subclass = 6
|
||||
command = ["ahcid", "$5"]
|
||||
command = ["initfs:bin/ahcid", "$BAR5", "$IRQ"]
|
||||
|
|
|
@ -72,7 +72,7 @@ impl Scheme for DebugScheme {
|
|||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn fevent(&self, _file: usize, flags: usize) -> Result<usize> {
|
||||
fn fevent(&self, _file: usize, _flags: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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<usize> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn physalloc(size: usize) -> Result<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
if size == 0 {
|
||||
|
@ -617,6 +627,14 @@ pub fn sched_yield() -> Result<usize> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
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<usize> {
|
||||
loop {
|
||||
{
|
||||
|
|
|
@ -114,6 +114,14 @@ pub fn open(path: &str, flags: usize) -> Result<usize> {
|
|||
unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) }
|
||||
}
|
||||
|
||||
pub unsafe fn physalloc(size: usize) -> Result<usize> {
|
||||
syscall1(SYS_PHYSALLOC, size)
|
||||
}
|
||||
|
||||
pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
syscall2(SYS_PHYSFREE, physical_address, size)
|
||||
}
|
||||
|
||||
pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> {
|
||||
syscall3(SYS_PHYSMAP, physical_address, size, flags)
|
||||
}
|
||||
|
@ -138,6 +146,10 @@ pub fn unlink(path: &str) -> Result<usize> {
|
|||
unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) }
|
||||
}
|
||||
|
||||
pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
syscall1(SYS_VIRTTOPHYS, virtual_address)
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> {
|
||||
unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue