Abstractions for better Ahci driver

This commit is contained in:
Jeremy Soller 2016-09-27 11:14:27 -06:00
parent a4df5185d2
commit f714d4858a
5 changed files with 151 additions and 71 deletions

View file

@ -40,7 +40,7 @@ FORCE:
# Emulation # Emulation
QEMU=qemu-system-$(ARCH) QEMU=qemu-system-$(ARCH)
QEMUFLAGS=-serial mon:stdio -d guest_errors QEMUFLAGS=-serial mon:stdio -d cpu_reset -d guest_errors
ifeq ($(ARCH),arm) ifeq ($(ARCH),arm)
LD=$(ARCH)-none-eabi-ld LD=$(ARCH)-none-eabi-ld
QEMUFLAGS+=-cpu arm1176 -machine integratorcp QEMUFLAGS+=-cpu arm1176 -machine integratorcp

View file

@ -0,0 +1,76 @@
use std::{mem, ptr};
use std::ops::{Deref, DerefMut};
use syscall::{self, Result};
struct PhysBox {
address: usize,
size: usize
}
impl PhysBox {
fn new(size: usize) -> Result<PhysBox> {
let address = unsafe { syscall::physalloc(size)? };
Ok(PhysBox {
address: address,
size: size
})
}
}
impl Drop for PhysBox {
fn drop(&mut self) {
let _ = unsafe { syscall::physfree(self.address, self.size) };
}
}
pub struct Dma<T> {
phys: PhysBox,
virt: *mut T
}
impl<T> Dma<T> {
pub fn new(value: T) -> Result<Dma<T>> {
let phys = PhysBox::new(mem::size_of::<T>())?;
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
unsafe { ptr::write(virt, value); }
Ok(Dma {
phys: phys,
virt: virt
})
}
pub fn zeroed() -> Result<Dma<T>> {
let phys = PhysBox::new(mem::size_of::<T>())?;
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
unsafe { ptr::write_bytes(virt as *mut u8, 0, phys.size); }
Ok(Dma {
phys: phys,
virt: virt
})
}
pub fn physical(&self) -> usize {
self.phys.address
}
}
impl<T> Deref for Dma<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.virt }
}
}
impl<T> DerefMut for Dma<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.virt }
}
}
impl<T> Drop for Dma<T> {
fn drop(&mut self) {
unsafe { drop(ptr::read(self.virt)); }
let _ = unsafe { syscall::physunmap(self.virt as usize) };
}
}

View file

@ -1,11 +1,13 @@
use io::{Io, Mmio}; use io::{Io, Mmio};
use std::mem::size_of; use std::mem::size_of;
use std::ops::DerefMut;
use std::{ptr, u32}; use std::{ptr, u32};
use syscall::{physalloc, physmap, physunmap, virttophys, MAP_WRITE}; use syscall::{virttophys, MAP_WRITE};
use syscall::error::{Error, Result, EIO}; use syscall::error::{Error, Result, EIO};
use super::dma::Dma;
use super::fis::{FisType, FisRegH2D}; use super::fis::{FisType, FisRegH2D};
const ATA_CMD_READ_DMA_EXT: u8 = 0x25; const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
@ -72,49 +74,42 @@ impl HbaPort {
} }
} }
pub fn init(&mut self) { pub fn init(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32], fb: &mut Dma<[u8; 256]>) {
self.stop(); self.stop();
let clb_phys = unsafe { physalloc(size_of::<HbaCmdHeader>()).unwrap() }; self.clb.write(clb.physical() as u64);
self.clb.write(clb_phys as u64); self.fb.write(fb.physical() 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 { for i in 0..32 {
let cmdheader = unsafe { &mut *clb.offset(i) }; let cmdheader = &mut clb[i];
let ctba = unsafe { physalloc(size_of::<HbaCmdTable>()).unwrap() }; cmdheader.ctba.write(ctbas[i].physical() as u64);
cmdheader.ctba.write(ctba as u64);
cmdheader.prdtl.write(0); cmdheader.prdtl.write(0);
} }
unsafe { physunmap(clb as usize).unwrap(); }
self.start(); self.start();
} }
pub unsafe fn identify(&mut self, port: usize) -> Option<u64> { pub unsafe fn identify(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32]) -> Option<u64> {
self.is.write(u32::MAX); self.is.write(u32::MAX);
let dest_phys = physalloc(256).unwrap(); let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap();
let dest = physmap(dest_phys, 256, MAP_WRITE).unwrap() as *mut u16;
if let Some(slot) = self.slot() { if let Some(slot) = self.slot() {
{ let cmdheader = &mut clb[slot as usize];
let clb = unsafe { physmap(self.clb.read() as usize, size_of::<HbaCmdHeader>(), MAP_WRITE).unwrap() } as *mut HbaCmdHeader; cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8));
let cmdheader = &mut *clb.offset(slot as isize); cmdheader.prdtl.write(1);
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 ctbas[slot as usize];
let cmdtbl = &mut *(ctba); ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>());
let prdt_entry = &mut cmdtbl.prdt_entry[0]; let prdt_entry = &mut cmdtbl.prdt_entry[0];
prdt_entry.dba.write(dest_phys as u64); prdt_entry.dba.write(dest.physical() as u64);
prdt_entry.dbc.write(512 | 1); prdt_entry.dbc.write(512 | 1);
}
let cmdfis = &mut *(cmdtbl.cfis.as_ptr() as *mut FisRegH2D); {
let cmdfis = &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D);
cmdfis.fis_type.write(FisType::RegH2D as u8); cmdfis.fis_type.write(FisType::RegH2D as u8);
cmdfis.pm.write(1 << 7); cmdfis.pm.write(1 << 7);
@ -122,9 +117,6 @@ impl HbaPort {
cmdfis.device.write(0); cmdfis.device.write(0);
cmdfis.countl.write(1); cmdfis.countl.write(1);
cmdfis.counth.write(0); 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) {} while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {}
@ -143,7 +135,7 @@ impl HbaPort {
let mut serial = String::new(); let mut serial = String::new();
for word in 10..20 { for word in 10..20 {
let d = *dest.offset(word); let d = dest[word];
let a = ((d >> 8) as u8) as char; let a = ((d >> 8) as u8) as char;
if a != '\0' { if a != '\0' {
serial.push(a); serial.push(a);
@ -156,7 +148,7 @@ impl HbaPort {
let mut firmware = String::new(); let mut firmware = String::new();
for word in 23..27 { for word in 23..27 {
let d = *dest.offset(word); let d = dest[word];
let a = ((d >> 8) as u8) as char; let a = ((d >> 8) as u8) as char;
if a != '\0' { if a != '\0' {
firmware.push(a); firmware.push(a);
@ -169,7 +161,7 @@ impl HbaPort {
let mut model = String::new(); let mut model = String::new();
for word in 27..47 { for word in 27..47 {
let d = *dest.offset(word); let d = dest[word];
let a = ((d >> 8) as u8) as char; let a = ((d >> 8) as u8) as char;
if a != '\0' { if a != '\0' {
model.push(a); model.push(a);
@ -180,20 +172,20 @@ impl HbaPort {
} }
} }
let mut sectors = (*dest.offset(100) as u64) | let mut sectors = (dest[100] as u64) |
((*dest.offset(101) as u64) << 16) | ((dest[101] as u64) << 16) |
((*dest.offset(102) as u64) << 32) | ((dest[102] as u64) << 32) |
((*dest.offset(103) as u64) << 48); ((dest[103] as u64) << 48);
let lba_bits = if sectors == 0 { let lba_bits = if sectors == 0 {
sectors = (*dest.offset(60) as u64) | ((*dest.offset(61) as u64) << 16); sectors = (dest[60] as u64) | ((dest[61] as u64) << 16);
28 28
} else { } else {
48 48
}; };
println!(" + Port {}: Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB", println!(" + Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB",
port, serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048); serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048);
Some(sectors * 512) Some(sectors * 512)
} else { } else {
@ -353,14 +345,14 @@ pub struct HbaMem {
} }
#[repr(packed)] #[repr(packed)]
struct HbaPrdtEntry { pub struct HbaPrdtEntry {
dba: Mmio<u64>, // Data base address dba: Mmio<u64>, // Data base address
rsv0: Mmio<u32>, // Reserved rsv0: Mmio<u32>, // Reserved
dbc: Mmio<u32>, // Byte count, 4M max, interrupt = 1 dbc: Mmio<u32>, // Byte count, 4M max, interrupt = 1
} }
#[repr(packed)] #[repr(packed)]
struct HbaCmdTable { pub struct HbaCmdTable {
// 0x00 // 0x00
cfis: [Mmio<u8>; 64], // Command FIS cfis: [Mmio<u8>; 64], // Command FIS
@ -375,7 +367,7 @@ struct HbaCmdTable {
} }
#[repr(packed)] #[repr(packed)]
struct HbaCmdHeader { pub struct HbaCmdHeader {
// DW0 // DW0
cfl: Mmio<u8>, /* Command FIS length in DWORDS, 2 ~ 16, atapi: 4, write - host to device: 2, prefetchable: 1 */ 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 pm: Mmio<u8>, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier

View file

@ -2,8 +2,10 @@ use io::Io;
use syscall::error::Result; use syscall::error::Result;
use self::hba::{HbaMem, HbaPort, HbaPortType}; use self::dma::Dma;
use self::hba::{HbaMem, HbaCmdTable, HbaCmdHeader, HbaPort, HbaPortType};
pub mod dma;
pub mod fis; pub mod fis;
pub mod hba; pub mod hba;
@ -17,17 +19,17 @@ impl Ahci {
let ret: Vec<AhciDisk> = (0..32) let ret: Vec<AhciDisk> = (0..32)
.filter(|&i| pi & 1 << i as i32 == 1 << i as i32) .filter(|&i| pi & 1 << i as i32 == 1 << i as i32)
.filter_map(|i| { .filter_map(|i| {
let mut disk = AhciDisk::new(base, i, irq); let port = &mut unsafe { &mut *(base as *mut HbaMem) }.ports[i];
let port_type = disk.port.probe(); let port_type = port.probe();
println!("{}: {:?}", i, port_type); println!("{}: {:?}", i, port_type);
match port_type { match port_type {
HbaPortType::SATA => { HbaPortType::SATA => {
disk.port.init(); match AhciDisk::new(port) {
if let Some(size) = unsafe { disk.port.identify(i) } { Ok(disk) => Some(disk),
disk.size = size; Err(err) => {
Some(disk) println!("{}: {}", i, err);
} else { None
None }
} }
} }
_ => None, _ => None,
@ -41,29 +43,38 @@ impl Ahci {
pub struct AhciDisk { pub struct AhciDisk {
port: &'static mut HbaPort, port: &'static mut HbaPort,
port_index: usize,
irq: u8,
size: u64, size: u64,
clb: Dma<[HbaCmdHeader; 32]>,
ctbas: [Dma<HbaCmdTable>; 32],
fb: Dma<[u8; 256]>
} }
impl AhciDisk { impl AhciDisk {
fn new(base: usize, port_index: usize, irq: u8) -> Self { fn new(port: &'static mut HbaPort) -> Result<Self> {
AhciDisk { let mut clb = Dma::zeroed()?;
port: &mut unsafe { &mut *(base as *mut HbaMem) }.ports[port_index], let mut ctbas = [
port_index: port_index, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
irq: irq, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
size: 0 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()?;
fn name(&self) -> String { port.init(&mut clb, &mut ctbas, &mut fb);
format!("AHCI Port {}", self.port_index)
}
fn on_irq(&mut self, irq: u8) { let size = unsafe { port.identify(&mut clb, &mut ctbas).unwrap_or(0) };
if irq == self.irq {
//debugln!("AHCI IRQ"); Ok(AhciDisk {
} port: port,
size: size,
clb: clb,
ctbas: ctbas,
fb: fb
})
} }
fn size(&self) -> u64 { fn size(&self) -> u64 {

View file

@ -1,4 +1,5 @@
#![feature(asm)] #![feature(asm)]
#![feature(question_mark)]
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
@ -27,9 +28,9 @@ fn main() {
} }
let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") }; let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") };
{ ahci::Ahci::disks(address, irq);
ahci::Ahci::disks(address, irq); loop {
let _ = syscall::sched_yield();
} }
unsafe { physunmap(address).expect("ahcid: failed to unmap address") };
}); });
} }