Replace IDE with AHCI driver

master
Jiajie Chen 6 years ago
parent cbba658e22
commit 2da17d7fcd

@ -66,7 +66,9 @@ qemu_net_opts := \
ifeq ($(arch), x86_64) ifeq ($(arch), x86_64)
qemu_opts += \ qemu_opts += \
-drive format=raw,file=$(bootimage) \ -drive format=raw,file=$(bootimage) \
-drive format=qcow2,file=$(SFSIMG),media=disk,cache=writeback \ -drive format=qcow2,file=$(SFSIMG),media=disk,cache=writeback,id=sfsimg,if=none \
-device ahci,id=ahci0 \
-device ide-drive,drive=sfsimg,bus=ahci0.0 \
-serial mon:stdio \ -serial mon:stdio \
-m 4G \ -m 4G \
-device isa-debug-exit -device isa-debug-exit

@ -2,19 +2,17 @@
//! //!
//! Spec: https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf //! Spec: https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf
use alloc::alloc::{alloc_zeroed, GlobalAlloc, Layout}; use alloc::alloc::{alloc_zeroed, Layout};
use alloc::boxed::Box;
use alloc::string::String; use alloc::string::String;
use alloc::sync::Arc; use alloc::sync::Arc;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cmp::min; use core::cmp::min;
use core::mem::{size_of, zeroed}; use core::mem::size_of;
use core::slice; use core::slice;
use core::str;
use crate::sync::FlagsGuard; use crate::sync::FlagsGuard;
use bitflags::*; use bitflags::*;
use device_tree::util::SliceRead;
use device_tree::Node;
use log::*; use log::*;
use rcore_memory::paging::PageTable; use rcore_memory::paging::PageTable;
use rcore_memory::PAGE_SIZE; use rcore_memory::PAGE_SIZE;
@ -22,10 +20,10 @@ use volatile::Volatile;
use rcore_fs::dev::BlockDevice; use rcore_fs::dev::BlockDevice;
use crate::drivers::BlockDriver;
use crate::memory::active_table; use crate::memory::active_table;
use crate::sync::SpinNoIrqLock as Mutex; use crate::sync::SpinNoIrqLock as Mutex;
use super::super::bus::virtio_mmio::*;
use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS}; use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS};
pub struct AHCI { pub struct AHCI {
@ -35,10 +33,12 @@ pub struct AHCI {
cmd_list: usize, cmd_list: usize,
cmd_table: usize, cmd_table: usize,
data: usize, data: usize,
port_addr: usize,
} }
pub struct AHCIDriver(Mutex<AHCI>); pub struct AHCIDriver(Mutex<AHCI>);
// AHCI
// 3.1 Generic Host Control // 3.1 Generic Host Control
#[repr(C)] #[repr(C)]
pub struct AHCIGHC { pub struct AHCIGHC {
@ -86,6 +86,7 @@ bitflags! {
} }
} }
// AHCI
// 3.3 Port Registers (one set per port) // 3.3 Port Registers (one set per port)
#[repr(C)] #[repr(C)]
pub struct AHCIPort { pub struct AHCIPort {
@ -109,6 +110,7 @@ pub struct AHCIPort {
devslp: Volatile<u32>, devslp: Volatile<u32>,
} }
// AHCi
// 4.2.1 Received FIS Structure // 4.2.1 Received FIS Structure
#[repr(C)] #[repr(C)]
pub struct AHCIRFIS { pub struct AHCIRFIS {
@ -120,6 +122,7 @@ pub struct AHCIRFIS {
reserved: [u8; 0x60], reserved: [u8; 0x60],
} }
// AHCI
// 4.2.2 Command List Structure // 4.2.2 Command List Structure
#[repr(C)] #[repr(C)]
pub struct AHCICommandHeader { pub struct AHCICommandHeader {
@ -132,6 +135,7 @@ pub struct AHCICommandHeader {
reservec: [u32; 4], reservec: [u32; 4],
} }
// AHCI
// 4.2.3 Command Table // 4.2.3 Command Table
#[repr(C)] #[repr(C)]
pub struct AHCICommandTable { pub struct AHCICommandTable {
@ -150,40 +154,49 @@ pub struct AHCIPRD {
dbc_i: u32, dbc_i: u32,
} }
const FIS_REG_H2D: u8 = 0x27;
const CMD_READ_DMA_EXT: u8 = 0x25;
const CMD_WRITE_DMA_EXT: u8 = 0x35;
const CMD_IDENTIFY_DEVICE: u8 = 0xec;
// https://wiki.osdev.org/AHCI // https://wiki.osdev.org/AHCI
// SATA
// Figure 5-2: Register FIS - Host to Device
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default)]
pub struct AHCIFISRegH2D { pub struct SATAFISRegH2D {
fis_type: u8, fis_type: u8,
cflags: u8, cflags: u8,
command: u8, command: u8,
feature_lo: u8, feature_lo: u8,
lba_0: u8, lba_0: u8, // LBA 7:0
lba_1: u8, lba_1: u8, // LBA 15:8
lba_2: u8, lba_2: u8, // LBA 23:16
device: u8, dev_head: u8,
lba_3: u8, lba_3: u8, // LBA 31:24
lba_4: u8, lba_4: u8, // LBA 39:32
lba_5: u8, lba_5: u8, // LBA 47:40
feature_hi: u8, feature_hi: u8,
count_lo: u8,
count_hi: u8,
icc: u8,
control: u8,
sector_count_lo: u8, sector_count_lo: u8,
sector_count_hi: u8, sector_count_hi: u8,
reserved: u8,
control: u8,
} }
// ATA8-ACS
// Table 29 IDENTIFY DEVICE data // Table 29 IDENTIFY DEVICE data
#[repr(C)] #[repr(C)]
pub struct AHCIIdentifyPacket { pub struct ATAIdentifyPacket {
_1: [u16; 10], _1: [u16; 10],
serial: [u8; 20], // words 10-19 serial: [u8; 20], // words 10-19
_2: [u16; 3], _2: [u16; 3],
firmware: [u8; 8], // words 23-26 firmware: [u8; 8], // words 23-26
model: [u8; 40], // words 27-46 model: [u8; 40], // words 27-46
_3: [u16; 16], _3: [u16; 13],
lba_sectors: u32, // words 60-61 lba_sectors: u32, // words 60-61
_4: [u16; 38],
lba48_sectors: u64, // words 100-103
} }
impl Driver for AHCIDriver { impl Driver for AHCIDriver {
@ -191,8 +204,15 @@ impl Driver for AHCIDriver {
let driver = self.0.lock(); let driver = self.0.lock();
// ensure header page is mapped // ensure header page is mapped
active_table().map_if_not_exists(driver.header as usize, driver.header as usize); let header = driver.header as usize;
unimplemented!(); let size = driver.size as usize;
if let None = active_table().get_entry(header) {
let mut current_addr = header;
while current_addr < header + size {
active_table().map_if_not_exists(current_addr, current_addr);
current_addr = current_addr + PAGE_SIZE;
}
}
return false; return false;
} }
@ -203,26 +223,130 @@ impl Driver for AHCIDriver {
fn get_id(&self) -> String { fn get_id(&self) -> String {
format!("ahci") format!("ahci")
} }
}
const BLOCK_SIZE: usize = 512; fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
impl BlockDevice for AHCIDriver {
const BLOCK_SIZE_LOG2: u8 = 9; // 512
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool {
let mut driver = self.0.lock(); let mut driver = self.0.lock();
// ensure header page is mapped // ensure header page is mapped
active_table().map_if_not_exists(driver.header as usize, driver.header as usize); let header = driver.header as usize;
unimplemented!() let size = driver.size as usize;
if let None = active_table().get_entry(header) {
let mut current_addr = header;
while current_addr < header + size {
active_table().map_if_not_exists(current_addr, current_addr);
current_addr = current_addr + PAGE_SIZE;
}
}
let port = unsafe { &mut *(driver.port_addr as *mut AHCIPort) };
let cmd_headers = unsafe {
slice::from_raw_parts_mut(
driver.cmd_list as *mut AHCICommandHeader,
PAGE_SIZE / size_of::<AHCICommandHeader>(),
)
};
cmd_headers[0].prdbc = 0;
cmd_headers[0].pwa_cfl = 0;
let cmd_table = unsafe { &mut *(driver.cmd_table as *mut AHCICommandTable) };
let len = min(BLOCK_SIZE, buf.len());
let data = unsafe { slice::from_raw_parts(driver.data as *const u8, len) };
let fis = unsafe { &mut *(cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) };
// Register FIS from HBA to device
fis.fis_type = FIS_REG_H2D;
fis.cflags = 1 << 7;
// 7.25 READ DMA EXT - 25h, DMA
fis.command = CMD_READ_DMA_EXT;
fis.sector_count_lo = 1;
fis.sector_count_hi = 0;
fis.dev_head = 0x40; // LBA
fis.control = 0x80; // LBA48
let block_id = block_id as u64; // avoid problems on riscv32
fis.lba_0 = block_id as u8;
fis.lba_1 = (block_id >> 8) as u8;
fis.lba_2 = (block_id >> 16) as u8;
fis.lba_3 = (block_id >> 24) as u8;
fis.lba_4 = (block_id >> 32) as u8;
fis.lba_5 = (block_id >> 40) as u8;
port.ci.write(1 << 0);
loop {
let ci = port.ci.read();
if (ci & (1 << 0)) == 0 {
break;
}
}
(&mut buf[..len]).clone_from_slice(&data);
return true;
} }
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool { fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
if buf.len() < BLOCK_SIZE {
return false;
}
let mut driver = self.0.lock(); let mut driver = self.0.lock();
// ensure header page is mapped // ensure header page is mapped
active_table().map_if_not_exists(driver.header as usize, driver.header as usize); let header = driver.header as usize;
unimplemented!() let size = driver.size as usize;
if let None = active_table().get_entry(header) {
let mut current_addr = header;
while current_addr < header + size {
active_table().map_if_not_exists(current_addr, current_addr);
current_addr = current_addr + PAGE_SIZE;
}
}
let port = unsafe { &mut *(driver.port_addr as *mut AHCIPort) };
let cmd_headers = unsafe {
slice::from_raw_parts_mut(
driver.cmd_list as *mut AHCICommandHeader,
PAGE_SIZE / size_of::<AHCICommandHeader>(),
)
};
cmd_headers[0].prdbc = 0;
cmd_headers[0].pwa_cfl = 1 << 6; // devic write
let cmd_table = unsafe { &mut *(driver.cmd_table as *mut AHCICommandTable) };
let data = unsafe { slice::from_raw_parts_mut(driver.data as *mut u8, BLOCK_SIZE) };
data.clone_from_slice(&buf[..BLOCK_SIZE]);
let fis = unsafe { &mut *(cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) };
// Register FIS from HBA to device
fis.fis_type = FIS_REG_H2D;
fis.cflags = 1 << 7;
// ATA8-ACS
// 7.63 WRITE DMA EXT - 35h, DMA
fis.command = CMD_WRITE_DMA_EXT;
fis.sector_count_lo = 1;
fis.sector_count_hi = 0;
fis.dev_head = 0x40; // LBA
fis.control = 0x80; // LBA48
let block_id = block_id as u64; // avoid problems on riscv32
fis.lba_0 = block_id as u8;
fis.lba_1 = (block_id >> 8) as u8;
fis.lba_2 = (block_id >> 16) as u8;
fis.lba_3 = (block_id >> 24) as u8;
fis.lba_4 = (block_id >> 32) as u8;
fis.lba_5 = (block_id >> 40) as u8;
port.ci.write(1 << 0);
loop {
let ci = port.ci.read();
if (ci & (1 << 0)) == 0 {
break;
}
}
return true;
} }
} }
const BLOCK_SIZE: usize = 512;
fn from_ata_string(data: &[u8]) -> String { fn from_ata_string(data: &[u8]) -> String {
let mut swapped_data = Vec::new(); let mut swapped_data = Vec::new();
assert!(data.len() % 2 == 0); assert!(data.len() % 2 == 0);
@ -327,39 +451,55 @@ pub fn ahci_init(irq: Option<u32>, header: usize, size: usize) -> Arc<AHCIDriver
warn!("port is not connected to external drive?"); warn!("port is not connected to external drive?");
} }
let fis = unsafe { &mut *(cmd_table.cfis.as_ptr() as *mut AHCIFISRegH2D) }; let fis = unsafe { &mut *(cmd_table.cfis.as_ptr() as *mut SATAFISRegH2D) };
// Register FIS from host to device // Register FIS from HBA to device
fis.fis_type = 0x27; fis.fis_type = FIS_REG_H2D;
fis.cflags = 1 << 7; fis.cflags = 1 << 7;
// 7.15 IDENTIFY DEVICE - ECh, PIO Data-In // 7.15 IDENTIFY DEVICE - ECh, PIO Data-In
fis.command = 0xec; // ide identify fis.command = CMD_IDENTIFY_DEVICE;
fis.sector_count_lo = 1; fis.sector_count_lo = 1;
debug!("issued identify command"); debug!("issued identify command");
port.ci.write(1 << 0); port.ci.write(1 << 0);
loop { loop {
let stat = port.tfd.read();
let ci = port.ci.read(); let ci = port.ci.read();
if stat & 0x80 == 0 || (ci & (1 << 0)) == 0 { if (ci & (1 << 0)) == 0 {
break; break;
} }
} }
let identify_data = unsafe { &*(data_va as *mut AHCIIdentifyPacket) }; let identify_data = unsafe { &*(data_va as *mut ATAIdentifyPacket) };
unsafe { unsafe {
debug!( debug!(
"{:?} {:?} {:?} {}", "Found ATA Device serial {} firmware {} model {} sectors 24bit={} 48bit={}",
from_ata_string(&identify_data.serial), from_ata_string(&identify_data.serial).trim_end(),
from_ata_string(&identify_data.firmware), from_ata_string(&identify_data.firmware).trim_end(),
from_ata_string(&identify_data.model), from_ata_string(&identify_data.model).trim_end(),
identify_data.lba_sectors identify_data.lba_sectors,
identify_data.lba48_sectors,
); );
} }
break; let driver = AHCIDriver(Mutex::new(AHCI {
header,
size,
rfis: rfis_va,
cmd_list: cmd_list_va,
cmd_table: cmd_table_va,
data: data_va,
port_addr: addr,
}));
let driver = Arc::new(driver);
DRIVERS.write().push(driver.clone());
BLK_DRIVERS
.write()
.push(Arc::new(BlockDriver(driver.clone())));
return driver;
} }
} }

@ -1,3 +1,4 @@
use alloc::boxed::Box;
use alloc::string::String; use alloc::string::String;
use alloc::sync::Arc; use alloc::sync::Arc;
use core::cmp::min; use core::cmp::min;
@ -14,6 +15,7 @@ use volatile::Volatile;
use rcore_fs::dev::BlockDevice; use rcore_fs::dev::BlockDevice;
use crate::drivers::BlockDriver;
use crate::memory::active_table; use crate::memory::active_table;
use crate::sync::SpinNoIrqLock as Mutex; use crate::sync::SpinNoIrqLock as Mutex;
@ -125,11 +127,8 @@ impl Driver for VirtIOBlkDriver {
fn get_id(&self) -> String { fn get_id(&self) -> String {
format!("virtio_block") format!("virtio_block")
} }
}
impl BlockDevice for VirtIOBlkDriver { fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
const BLOCK_SIZE_LOG2: u8 = 9; // 512
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool {
let mut driver = self.0.lock(); let mut driver = self.0.lock();
// ensure header page is mapped // ensure header page is mapped
active_table().map_if_not_exists(driver.header as usize, driver.header as usize); active_table().map_if_not_exists(driver.header as usize, driver.header as usize);
@ -157,7 +156,7 @@ impl BlockDevice for VirtIOBlkDriver {
} }
} }
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool { fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
let mut driver = self.0.lock(); let mut driver = self.0.lock();
// ensure header page is mapped // ensure header page is mapped
active_table().map_if_not_exists(driver.header as usize, driver.header as usize); active_table().map_if_not_exists(driver.header as usize, driver.header as usize);
@ -226,5 +225,5 @@ pub fn virtio_blk_init(node: &Node) {
let driver = Arc::new(driver); let driver = Arc::new(driver);
DRIVERS.write().push(driver.clone()); DRIVERS.write().push(driver.clone());
BLK_DRIVERS.write().push(driver); BLK_DRIVERS.write().push(Arc::new(BlockDriver(driver)));
} }

@ -5,8 +5,8 @@ use lazy_static::lazy_static;
use smoltcp::wire::{EthernetAddress, Ipv4Address}; use smoltcp::wire::{EthernetAddress, Ipv4Address};
use spin::RwLock; use spin::RwLock;
use self::block::virtio_blk::VirtIOBlkDriver;
use crate::sync::Condvar; use crate::sync::Condvar;
use rcore_fs::dev::BlockDevice;
#[allow(dead_code)] #[allow(dead_code)]
pub mod block; pub mod block;
@ -64,13 +64,35 @@ pub trait Driver: Send + Sync {
fn poll(&self) { fn poll(&self) {
unimplemented!("not a net driver") unimplemented!("not a net driver")
} }
// block related drivers should implement these
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
unimplemented!("not a block driver")
}
fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
unimplemented!("not a block driver")
}
} }
lazy_static! { lazy_static! {
// NOTE: RwLock only write when initializing drivers // NOTE: RwLock only write when initializing drivers
pub static ref DRIVERS: RwLock<Vec<Arc<Driver>>> = RwLock::new(Vec::new()); pub static ref DRIVERS: RwLock<Vec<Arc<Driver>>> = RwLock::new(Vec::new());
pub static ref NET_DRIVERS: RwLock<Vec<Arc<Driver>>> = RwLock::new(Vec::new()); pub static ref NET_DRIVERS: RwLock<Vec<Arc<Driver>>> = RwLock::new(Vec::new());
pub static ref BLK_DRIVERS: RwLock<Vec<Arc<VirtIOBlkDriver>>> = RwLock::new(Vec::new()); pub static ref BLK_DRIVERS: RwLock<Vec<Arc<BlockDriver>>> = RwLock::new(Vec::new());
}
pub struct BlockDriver(Arc<Driver>);
impl BlockDevice for BlockDriver {
const BLOCK_SIZE_LOG2: u8 = 9; // 512
fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool {
self.0.read_block(block_id, buf)
}
fn write_at(&self, block_id: usize, buf: &[u8]) -> bool {
self.0.write_block(block_id, buf)
}
} }
lazy_static! { lazy_static! {

@ -20,7 +20,6 @@ use crate::memory::active_table;
use crate::net::SOCKETS; use crate::net::SOCKETS;
use crate::sync::FlagsGuard; use crate::sync::FlagsGuard;
use crate::sync::SpinNoIrqLock as Mutex; use crate::sync::SpinNoIrqLock as Mutex;
use crate::sync::{MutexGuard, SpinNoIrq};
use super::super::{provider::Provider, DeviceType, Driver, DRIVERS, NET_DRIVERS, SOCKET_ACTIVITY}; use super::super::{provider::Provider, DeviceType, Driver, DRIVERS, NET_DRIVERS, SOCKET_ACTIVITY};

@ -37,15 +37,15 @@ lazy_static! {
pub static ref ROOT_INODE: Arc<INode> = { pub static ref ROOT_INODE: Arc<INode> = {
#[cfg(not(feature = "link_user"))] #[cfg(not(feature = "link_user"))]
let device = { let device = {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] #[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "x86_64"))]
{ {
crate::drivers::BLK_DRIVERS.read().iter() crate::drivers::BLK_DRIVERS.read().iter()
.next().expect("VirtIOBlk not found") .next().expect("Block device not found")
.clone() .clone()
} }
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "aarch64")]
{ {
Arc::new(ide::IDE::new(1)) unimplemented!()
} }
}; };
#[cfg(feature = "link_user")] #[cfg(feature = "link_user")]

Loading…
Cancel
Save