Abstractions for better Ahci driver
This commit is contained in:
parent
a4df5185d2
commit
f714d4858a
2
Makefile
2
Makefile
|
@ -40,7 +40,7 @@ FORCE:
|
|||
|
||||
# Emulation
|
||||
QEMU=qemu-system-$(ARCH)
|
||||
QEMUFLAGS=-serial mon:stdio -d guest_errors
|
||||
QEMUFLAGS=-serial mon:stdio -d cpu_reset -d guest_errors
|
||||
ifeq ($(ARCH),arm)
|
||||
LD=$(ARCH)-none-eabi-ld
|
||||
QEMUFLAGS+=-cpu arm1176 -machine integratorcp
|
||||
|
|
76
drivers/ahcid/src/ahci/dma.rs
Normal file
76
drivers/ahcid/src/ahci/dma.rs
Normal 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) };
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
use io::{Io, Mmio};
|
||||
|
||||
use std::mem::size_of;
|
||||
use std::ops::DerefMut;
|
||||
use std::{ptr, u32};
|
||||
|
||||
use syscall::{physalloc, physmap, physunmap, virttophys, MAP_WRITE};
|
||||
use syscall::{virttophys, MAP_WRITE};
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::dma::Dma;
|
||||
use super::fis::{FisType, FisRegH2D};
|
||||
|
||||
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();
|
||||
|
||||
let clb_phys = unsafe { physalloc(size_of::<HbaCmdHeader>()).unwrap() };
|
||||
self.clb.write(clb_phys as u64);
|
||||
self.clb.write(clb.physical() 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 {
|
||||
let cmdheader = unsafe { &mut *clb.offset(i) };
|
||||
let ctba = unsafe { physalloc(size_of::<HbaCmdTable>()).unwrap() };
|
||||
cmdheader.ctba.write(ctba as u64);
|
||||
let cmdheader = &mut clb[i];
|
||||
cmdheader.ctba.write(ctbas[i].physical() as u64);
|
||||
cmdheader.prdtl.write(0);
|
||||
}
|
||||
unsafe { physunmap(clb as usize).unwrap(); }
|
||||
|
||||
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);
|
||||
|
||||
let dest_phys = physalloc(256).unwrap();
|
||||
let dest = physmap(dest_phys, 256, MAP_WRITE).unwrap() as *mut u16;
|
||||
let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap();
|
||||
|
||||
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);
|
||||
let cmdheader = &mut clb[slot as usize];
|
||||
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 cmdtbl = &mut ctbas[slot as usize];
|
||||
ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.pm.write(1 << 7);
|
||||
|
@ -122,9 +117,6 @@ impl HbaPort {
|
|||
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) {}
|
||||
|
@ -143,7 +135,7 @@ impl HbaPort {
|
|||
|
||||
let mut serial = String::new();
|
||||
for word in 10..20 {
|
||||
let d = *dest.offset(word);
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
serial.push(a);
|
||||
|
@ -156,7 +148,7 @@ impl HbaPort {
|
|||
|
||||
let mut firmware = String::new();
|
||||
for word in 23..27 {
|
||||
let d = *dest.offset(word);
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
firmware.push(a);
|
||||
|
@ -169,7 +161,7 @@ impl HbaPort {
|
|||
|
||||
let mut model = String::new();
|
||||
for word in 27..47 {
|
||||
let d = *dest.offset(word);
|
||||
let d = dest[word];
|
||||
let a = ((d >> 8) as u8) as char;
|
||||
if a != '\0' {
|
||||
model.push(a);
|
||||
|
@ -180,20 +172,20 @@ impl HbaPort {
|
|||
}
|
||||
}
|
||||
|
||||
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 mut sectors = (dest[100] as u64) |
|
||||
((dest[101] as u64) << 16) |
|
||||
((dest[102] as u64) << 32) |
|
||||
((dest[103] as u64) << 48);
|
||||
|
||||
let lba_bits = if sectors == 0 {
|
||||
sectors = (*dest.offset(60) as u64) | ((*dest.offset(61) as u64) << 16);
|
||||
sectors = (dest[60] as u64) | ((dest[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);
|
||||
println!(" + Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB",
|
||||
serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048);
|
||||
|
||||
Some(sectors * 512)
|
||||
} else {
|
||||
|
@ -353,14 +345,14 @@ pub struct HbaMem {
|
|||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct HbaPrdtEntry {
|
||||
pub 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 {
|
||||
pub struct HbaCmdTable {
|
||||
// 0x00
|
||||
cfis: [Mmio<u8>; 64], // Command FIS
|
||||
|
||||
|
@ -375,7 +367,7 @@ struct HbaCmdTable {
|
|||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct HbaCmdHeader {
|
||||
pub 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
|
||||
|
|
|
@ -2,8 +2,10 @@ use io::Io;
|
|||
|
||||
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 hba;
|
||||
|
||||
|
@ -17,19 +19,19 @@ impl Ahci {
|
|||
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();
|
||||
let port = &mut unsafe { &mut *(base as *mut HbaMem) }.ports[i];
|
||||
let port_type = 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 {
|
||||
match AhciDisk::new(port) {
|
||||
Ok(disk) => Some(disk),
|
||||
Err(err) => {
|
||||
println!("{}: {}", i, err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
@ -41,29 +43,38 @@ impl Ahci {
|
|||
|
||||
pub struct AhciDisk {
|
||||
port: &'static mut HbaPort,
|
||||
port_index: usize,
|
||||
irq: u8,
|
||||
size: u64,
|
||||
clb: Dma<[HbaCmdHeader; 32]>,
|
||||
ctbas: [Dma<HbaCmdTable>; 32],
|
||||
fb: Dma<[u8; 256]>
|
||||
}
|
||||
|
||||
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 new(port: &'static mut HbaPort) -> Result<Self> {
|
||||
let mut clb = Dma::zeroed()?;
|
||||
let mut ctbas = [
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
|
||||
];
|
||||
let mut fb = Dma::zeroed()?;
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("AHCI Port {}", self.port_index)
|
||||
}
|
||||
port.init(&mut clb, &mut ctbas, &mut fb);
|
||||
|
||||
fn on_irq(&mut self, irq: u8) {
|
||||
if irq == self.irq {
|
||||
//debugln!("AHCI IRQ");
|
||||
}
|
||||
let size = unsafe { port.identify(&mut clb, &mut ctbas).unwrap_or(0) };
|
||||
|
||||
Ok(AhciDisk {
|
||||
port: port,
|
||||
size: size,
|
||||
clb: clb,
|
||||
ctbas: ctbas,
|
||||
fb: fb
|
||||
})
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#![feature(asm)]
|
||||
#![feature(question_mark)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
@ -27,9 +28,9 @@ fn main() {
|
|||
}
|
||||
|
||||
let address = unsafe { physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") };
|
||||
{
|
||||
ahci::Ahci::disks(address, irq);
|
||||
loop {
|
||||
let _ = syscall::sched_yield();
|
||||
}
|
||||
unsafe { physunmap(address).expect("ahcid: failed to unmap address") };
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue