Abstractions for better Ahci driver
This commit is contained in:
parent
a4df5185d2
commit
f714d4858a
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
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 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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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") };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue