From 2da17d7fcd549f94282172610ac8296219c24677 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Sat, 30 Mar 2019 10:03:08 +0800 Subject: [PATCH] Replace IDE with AHCI driver --- kernel/Makefile | 4 +- kernel/src/drivers/block/ahci.rs | 230 ++++++++++++++++++++----- kernel/src/drivers/block/virtio_blk.rs | 11 +- kernel/src/drivers/mod.rs | 26 ++- kernel/src/drivers/net/ixgbe.rs | 1 - kernel/src/fs/mod.rs | 8 +- 6 files changed, 221 insertions(+), 59 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index f1c3281..8a3bbf9 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -66,7 +66,9 @@ qemu_net_opts := \ ifeq ($(arch), x86_64) qemu_opts += \ -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 \ -m 4G \ -device isa-debug-exit diff --git a/kernel/src/drivers/block/ahci.rs b/kernel/src/drivers/block/ahci.rs index a84e577..74ba464 100644 --- a/kernel/src/drivers/block/ahci.rs +++ b/kernel/src/drivers/block/ahci.rs @@ -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 -use alloc::alloc::{alloc_zeroed, GlobalAlloc, Layout}; +use alloc::alloc::{alloc_zeroed, Layout}; +use alloc::boxed::Box; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use core::cmp::min; -use core::mem::{size_of, zeroed}; +use core::mem::size_of; use core::slice; -use core::str; use crate::sync::FlagsGuard; use bitflags::*; -use device_tree::util::SliceRead; -use device_tree::Node; use log::*; use rcore_memory::paging::PageTable; use rcore_memory::PAGE_SIZE; @@ -22,10 +20,10 @@ use volatile::Volatile; use rcore_fs::dev::BlockDevice; +use crate::drivers::BlockDriver; use crate::memory::active_table; use crate::sync::SpinNoIrqLock as Mutex; -use super::super::bus::virtio_mmio::*; use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS}; pub struct AHCI { @@ -35,10 +33,12 @@ pub struct AHCI { cmd_list: usize, cmd_table: usize, data: usize, + port_addr: usize, } pub struct AHCIDriver(Mutex); +// AHCI // 3.1 Generic Host Control #[repr(C)] pub struct AHCIGHC { @@ -86,6 +86,7 @@ bitflags! { } } +// AHCI // 3.3 Port Registers (one set per port) #[repr(C)] pub struct AHCIPort { @@ -109,6 +110,7 @@ pub struct AHCIPort { devslp: Volatile, } +// AHCi // 4.2.1 Received FIS Structure #[repr(C)] pub struct AHCIRFIS { @@ -120,6 +122,7 @@ pub struct AHCIRFIS { reserved: [u8; 0x60], } +// AHCI // 4.2.2 Command List Structure #[repr(C)] pub struct AHCICommandHeader { @@ -132,6 +135,7 @@ pub struct AHCICommandHeader { reservec: [u32; 4], } +// AHCI // 4.2.3 Command Table #[repr(C)] pub struct AHCICommandTable { @@ -150,40 +154,49 @@ pub struct AHCIPRD { 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 +// SATA +// Figure 5-2: Register FIS - Host to Device #[repr(C)] #[derive(Default)] -pub struct AHCIFISRegH2D { +pub struct SATAFISRegH2D { fis_type: u8, cflags: u8, command: u8, feature_lo: u8, - lba_0: u8, - lba_1: u8, - lba_2: u8, - device: u8, - lba_3: u8, - lba_4: u8, - lba_5: u8, + lba_0: u8, // LBA 7:0 + lba_1: u8, // LBA 15:8 + lba_2: u8, // LBA 23:16 + dev_head: u8, + lba_3: u8, // LBA 31:24 + lba_4: u8, // LBA 39:32 + lba_5: u8, // LBA 47:40 feature_hi: u8, - count_lo: u8, - count_hi: u8, - icc: u8, - control: u8, sector_count_lo: u8, sector_count_hi: u8, + reserved: u8, + control: u8, } +// ATA8-ACS // Table 29 IDENTIFY DEVICE data #[repr(C)] -pub struct AHCIIdentifyPacket { +pub struct ATAIdentifyPacket { _1: [u16; 10], serial: [u8; 20], // words 10-19 _2: [u16; 3], firmware: [u8; 8], // words 23-26 model: [u8; 40], // words 27-46 - _3: [u16; 16], + _3: [u16; 13], lba_sectors: u32, // words 60-61 + _4: [u16; 38], + lba48_sectors: u64, // words 100-103 } impl Driver for AHCIDriver { @@ -191,8 +204,15 @@ impl Driver for AHCIDriver { let driver = self.0.lock(); // ensure header page is mapped - active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - unimplemented!(); + let header = driver.header as usize; + 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; } @@ -203,26 +223,130 @@ impl Driver for AHCIDriver { fn get_id(&self) -> String { format!("ahci") } -} -const BLOCK_SIZE: usize = 512; -impl BlockDevice for AHCIDriver { - const BLOCK_SIZE_LOG2: u8 = 9; // 512 - fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool { + fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool { let mut driver = self.0.lock(); // ensure header page is mapped - active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - unimplemented!() + let header = driver.header as usize; + 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::(), + ) + }; + 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(); // ensure header page is mapped - active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - unimplemented!() + let header = driver.header as usize; + 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::(), + ) + }; + 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 { let mut swapped_data = Vec::new(); assert!(data.len() % 2 == 0); @@ -327,39 +451,55 @@ pub fn ahci_init(irq: Option, header: usize, size: usize) -> Arc String { format!("virtio_block") } -} -impl BlockDevice for VirtIOBlkDriver { - const BLOCK_SIZE_LOG2: u8 = 9; // 512 - fn read_at(&self, block_id: usize, buf: &mut [u8]) -> bool { + fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool { let mut driver = self.0.lock(); // ensure header page is mapped 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(); // ensure header page is mapped 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); DRIVERS.write().push(driver.clone()); - BLK_DRIVERS.write().push(driver); + BLK_DRIVERS.write().push(Arc::new(BlockDriver(driver))); } diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index 9a1f3e0..1363330 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -5,8 +5,8 @@ use lazy_static::lazy_static; use smoltcp::wire::{EthernetAddress, Ipv4Address}; use spin::RwLock; -use self::block::virtio_blk::VirtIOBlkDriver; use crate::sync::Condvar; +use rcore_fs::dev::BlockDevice; #[allow(dead_code)] pub mod block; @@ -64,13 +64,35 @@ pub trait Driver: Send + Sync { fn poll(&self) { 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! { // NOTE: RwLock only write when initializing drivers pub static ref DRIVERS: RwLock>> = RwLock::new(Vec::new()); pub static ref NET_DRIVERS: RwLock>> = RwLock::new(Vec::new()); - pub static ref BLK_DRIVERS: RwLock>> = RwLock::new(Vec::new()); + pub static ref BLK_DRIVERS: RwLock>> = RwLock::new(Vec::new()); +} + +pub struct BlockDriver(Arc); + +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! { diff --git a/kernel/src/drivers/net/ixgbe.rs b/kernel/src/drivers/net/ixgbe.rs index 284f457..fd96b7d 100644 --- a/kernel/src/drivers/net/ixgbe.rs +++ b/kernel/src/drivers/net/ixgbe.rs @@ -20,7 +20,6 @@ use crate::memory::active_table; use crate::net::SOCKETS; use crate::sync::FlagsGuard; use crate::sync::SpinNoIrqLock as Mutex; -use crate::sync::{MutexGuard, SpinNoIrq}; use super::super::{provider::Provider, DeviceType, Driver, DRIVERS, NET_DRIVERS, SOCKET_ACTIVITY}; diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 488dcb2..9414c47 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -37,15 +37,15 @@ lazy_static! { pub static ref ROOT_INODE: Arc = { #[cfg(not(feature = "link_user"))] 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() - .next().expect("VirtIOBlk not found") + .next().expect("Block device not found") .clone() } - #[cfg(target_arch = "x86_64")] + #[cfg(target_arch = "aarch64")] { - Arc::new(ide::IDE::new(1)) + unimplemented!() } }; #[cfg(feature = "link_user")]