Split into multiple files
This commit is contained in:
parent
09fa549dea
commit
4f6eb0080b
117
src/ata_command.rs
Normal file
117
src/ata_command.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use binread::BinRead;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ahci_structs::{DeviceRegsWriteBuilder, RegH2DFis, RegH2DFisBuilder},
|
||||||
|
identify::{IdentifyData, IdentifyPacketData},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
pub trait AtaCommandDataIn {
|
||||||
|
type ProcessedData<'a>;
|
||||||
|
fn get_fis(&self) -> RegH2DFis;
|
||||||
|
fn data_len(&self) -> usize;
|
||||||
|
fn process_data<'a>(&self, data: &'a [u8]) -> Self::ProcessedData<'a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IdentifyCommand;
|
||||||
|
|
||||||
|
impl AtaCommandDataIn for IdentifyCommand {
|
||||||
|
type ProcessedData<'a> = IdentifyData;
|
||||||
|
|
||||||
|
fn get_fis(&self) -> RegH2DFis {
|
||||||
|
RegH2DFisBuilder::default()
|
||||||
|
.cmd(true)
|
||||||
|
.regs(
|
||||||
|
DeviceRegsWriteBuilder::default()
|
||||||
|
.commmad(0xEC)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_len(&self) -> usize {
|
||||||
|
512
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_data(&self, data: &[u8]) -> Self::ProcessedData<'_> {
|
||||||
|
IdentifyData::read(&mut Cursor::new(data)).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IdentifyPacketCommand;
|
||||||
|
|
||||||
|
impl AtaCommandDataIn for IdentifyPacketCommand {
|
||||||
|
type ProcessedData<'a> = IdentifyPacketData;
|
||||||
|
|
||||||
|
fn get_fis(&self) -> RegH2DFis {
|
||||||
|
RegH2DFisBuilder::default()
|
||||||
|
.cmd(true)
|
||||||
|
.regs(
|
||||||
|
DeviceRegsWriteBuilder::default()
|
||||||
|
.commmad(0xA1)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_len(&self) -> usize {
|
||||||
|
512
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_data(&self, data: &[u8]) -> Self::ProcessedData<'_> {
|
||||||
|
IdentifyPacketData::read(&mut Cursor::new(data)).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReadDmaExtCommand {
|
||||||
|
lba: u64,
|
||||||
|
count: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadDmaExtCommand {
|
||||||
|
pub fn new(lba: u64, count: u16) -> Self {
|
||||||
|
Self { lba, count }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtaCommandDataIn for ReadDmaExtCommand {
|
||||||
|
type ProcessedData<'a> = &'a [u8];
|
||||||
|
|
||||||
|
fn get_fis(&self) -> RegH2DFis {
|
||||||
|
let lba_bytes = self.lba.to_le_bytes();
|
||||||
|
|
||||||
|
RegH2DFisBuilder::default()
|
||||||
|
.cmd(true)
|
||||||
|
.regs(
|
||||||
|
DeviceRegsWriteBuilder::default()
|
||||||
|
.commmad(0x25)
|
||||||
|
.lba(true)
|
||||||
|
.countl(self.count as u8)
|
||||||
|
.counth((self.count >> 8) as u8)
|
||||||
|
.lba0(lba_bytes[0])
|
||||||
|
.lba1(lba_bytes[1])
|
||||||
|
.lba2(lba_bytes[2])
|
||||||
|
.lba3(lba_bytes[3])
|
||||||
|
.lba4(lba_bytes[4])
|
||||||
|
.lba5(lba_bytes[5])
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_len(&self) -> usize {
|
||||||
|
(self.count * 512) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_data<'a>(&self, data: &'a [u8]) -> Self::ProcessedData<'a> {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
1
src/ata_dev.rs
Normal file
1
src/ata_dev.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
127
src/hba.rs
Normal file
127
src/hba.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use std::os::mikros::address_space::ACTIVE_SPACE;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ahci_structs::{CommandHeader, FisBuf, GenHC, PortRegs, CAP, GHC},
|
||||||
|
port::AhciPort,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Hba {
|
||||||
|
regs: &'static GenHC,
|
||||||
|
ports: Vec<AhciPort>,
|
||||||
|
syslog_client: syslog_rpc::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum HBAInitError {
|
||||||
|
No64BitDma,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hba {
|
||||||
|
pub unsafe fn new(
|
||||||
|
reg_base: *mut u8,
|
||||||
|
syslog_client: syslog_rpc::Client,
|
||||||
|
) -> Result<Self, HBAInitError> {
|
||||||
|
let regs = unsafe { &*(reg_base as *const GenHC) };
|
||||||
|
|
||||||
|
let mut hba = Self {
|
||||||
|
regs,
|
||||||
|
ports: Vec::new(),
|
||||||
|
syslog_client,
|
||||||
|
};
|
||||||
|
let supports_64bit_dma = regs.CAP.read(CAP::S64A) > 0;
|
||||||
|
if supports_64bit_dma {
|
||||||
|
syslog_client
|
||||||
|
.send_text_message("ahci", "HBA supports 64bit DMA".to_string())
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
syslog_client
|
||||||
|
.send_text_message("ahci", "HBA does not support 64bit DMA".to_string())
|
||||||
|
.unwrap();
|
||||||
|
syslog_client
|
||||||
|
.send_text_message(
|
||||||
|
"ahci",
|
||||||
|
"Aborting, there is no way to ensure buffers from the OS are in the low 4G."
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
return Err(HBAInitError::No64BitDma);
|
||||||
|
}
|
||||||
|
hba.reset();
|
||||||
|
let num_raw_ports = (regs.CAP.read(CAP::NP) + 1) as usize;
|
||||||
|
let port_reg_base = unsafe { reg_base.add(0x100).cast::<PortRegs>() };
|
||||||
|
let num_ports = (regs.PI.get().count_ones()) as usize;
|
||||||
|
if num_ports == num_raw_ports {
|
||||||
|
syslog_client
|
||||||
|
.send_text_message("ahci", format!("HBA has {num_ports} ports"))
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
syslog_client
|
||||||
|
.send_text_message(
|
||||||
|
"ahci",
|
||||||
|
format!("HBA has {num_raw_ports} ports, but only {num_ports} are usable",),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_cmd_slots = (regs.CAP.read(CAP::NCS) + 1) as usize;
|
||||||
|
|
||||||
|
let fis_buf_mem = num_ports * 256;
|
||||||
|
let comm_list_mem_per_port = max_cmd_slots * 32;
|
||||||
|
let comm_list_mem = comm_list_mem_per_port * num_ports;
|
||||||
|
let total_mem = fis_buf_mem + comm_list_mem;
|
||||||
|
|
||||||
|
let (buf, buf_phys) = ACTIVE_SPACE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.map_free_cont_phys(total_mem.div_ceil(4096))
|
||||||
|
.unwrap();
|
||||||
|
unsafe {
|
||||||
|
buf.write_bytes(0, total_mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ports_raw = regs.PI.get();
|
||||||
|
|
||||||
|
for port_idx in 0..32 {
|
||||||
|
if (ports_raw & (1 << port_idx)) == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let regs = unsafe { &*(port_reg_base.add(port_idx)) };
|
||||||
|
let cmd_list_phys = buf_phys + (comm_list_mem_per_port * hba.ports.len()) as u64;
|
||||||
|
let cmd_list = unsafe {
|
||||||
|
buf.add(comm_list_mem_per_port * hba.ports.len())
|
||||||
|
.cast::<CommandHeader>()
|
||||||
|
};
|
||||||
|
let fis_buf_phys = buf_phys + (comm_list_mem + 256 * hba.ports.len()) as u64;
|
||||||
|
let fis_buf = unsafe { FisBuf::new(buf.add(comm_list_mem + 256 * hba.ports.len())) };
|
||||||
|
|
||||||
|
hba.ports.push(AhciPort::new(
|
||||||
|
regs,
|
||||||
|
port_idx,
|
||||||
|
fis_buf,
|
||||||
|
fis_buf_phys,
|
||||||
|
cmd_list,
|
||||||
|
cmd_list_phys,
|
||||||
|
max_cmd_slots,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(hba)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&self) {
|
||||||
|
self.syslog_client
|
||||||
|
.send_text_message("ahci", "Resetting HBA".to_string())
|
||||||
|
.unwrap();
|
||||||
|
self.regs.GHC.modify(GHC::AE::SET);
|
||||||
|
self.regs.GHC.modify(GHC::HR::SET);
|
||||||
|
while self.regs.GHC.read(GHC::HR) > 0 {}
|
||||||
|
self.syslog_client
|
||||||
|
.send_text_message("ahci", "Reset HBA".to_string())
|
||||||
|
.unwrap();
|
||||||
|
self.regs.GHC.modify(GHC::AE::SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ports(&self) -> &[AhciPort] {
|
||||||
|
&self.ports
|
||||||
|
}
|
||||||
|
}
|
@ -162,7 +162,7 @@ pub struct IdentifyData {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, BinRead)]
|
#[derive(Clone, Debug, BinRead)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct IdentifyDataATAPI {
|
pub struct IdentifyPacketData {
|
||||||
#[br(seek_before = SeekFrom::Start(0))]
|
#[br(seek_before = SeekFrom::Start(0))]
|
||||||
pub gen_cfg: u16,
|
pub gen_cfg: u16,
|
||||||
#[br(seek_before = SeekFrom::Start(2*2))]
|
#[br(seek_before = SeekFrom::Start(2*2))]
|
||||||
|
456
src/main.rs
456
src/main.rs
@ -20,318 +20,22 @@
|
|||||||
#![allow(clippy::tuple_array_conversions)]
|
#![allow(clippy::tuple_array_conversions)]
|
||||||
|
|
||||||
mod ahci_structs;
|
mod ahci_structs;
|
||||||
|
mod ata_command;
|
||||||
|
mod ata_dev;
|
||||||
|
mod hba;
|
||||||
mod identify;
|
mod identify;
|
||||||
|
mod port;
|
||||||
|
|
||||||
use std::{
|
use std::os::mikros::{address_space::ACTIVE_SPACE, syscalls};
|
||||||
io::Cursor,
|
|
||||||
os::mikros::{address_space::ACTIVE_SPACE, syscalls},
|
|
||||||
ptr::NonNull,
|
|
||||||
};
|
|
||||||
|
|
||||||
use ahci_structs::{
|
use ata_command::{IdentifyCommand, IdentifyPacketCommand, ReadDmaExtCommand};
|
||||||
CommandHeader, CommandTableHeader, DeviceRegsWriteBuilder, FisBuf, GenHC, PortRegs, Prd, PxCMD,
|
use hba::Hba;
|
||||||
PxIS, PxSERR, PxSSTS, PxTFD, RegH2DFis, RegH2DFisBuilder, CAP, GHC,
|
|
||||||
};
|
|
||||||
use binread::BinRead;
|
|
||||||
use identify::{IdentifyData, IdentifyDataATAPI};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use volatile::VolatilePtr;
|
|
||||||
use x86_64::structures::paging::PageTableFlags;
|
use x86_64::structures::paging::PageTableFlags;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
struct AhciPort {
|
|
||||||
regs: &'static PortRegs,
|
|
||||||
phys_no: usize,
|
|
||||||
fis_buf: FisBuf,
|
|
||||||
cmd_list: *mut CommandHeader,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
cmd_list_len: usize,
|
|
||||||
has_device: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
enum CommandIssueError {
|
|
||||||
PrdtTooBig,
|
|
||||||
DataTooBig,
|
|
||||||
CommandFailed,
|
|
||||||
DataTooSmall,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AhciPort {
|
|
||||||
|
|
||||||
unsafe fn issue_command_prdt(
|
|
||||||
&self,
|
|
||||||
fis: &RegH2DFis,
|
|
||||||
prdt: &[Prd],
|
|
||||||
) -> Result<(), CommandIssueError> {
|
|
||||||
if prdt.len() > 65535 {
|
|
||||||
return Err(CommandIssueError::PrdtTooBig);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tbl_hdr = CommandTableHeader {
|
|
||||||
cfis: [0; 64],
|
|
||||||
acmd: [0; 16],
|
|
||||||
rsvd: [0; 48],
|
|
||||||
};
|
|
||||||
|
|
||||||
tbl_hdr.cfis[0..RegH2DFis::BYTE_SIZE].copy_from_slice(&fis.to_bytes());
|
|
||||||
|
|
||||||
let tbl_size = std::mem::size_of::<CommandTableHeader>() + std::mem::size_of_val(prdt);
|
|
||||||
|
|
||||||
let (buf, buf_phys) = ACTIVE_SPACE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.map_free_cont_phys(tbl_size.div_ceil(4096))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let tbl_hdr_ptr = buf.cast::<CommandTableHeader>();
|
|
||||||
let prdt_ptr = unsafe {
|
|
||||||
buf.add(std::mem::size_of::<CommandTableHeader>())
|
|
||||||
.cast::<Prd>()
|
|
||||||
};
|
|
||||||
let cmd_hdr_ptr = self.cmd_list;
|
|
||||||
|
|
||||||
let cmd_hdr = CommandHeader {
|
|
||||||
flags: (RegH2DFis::BYTE_SIZE / 4) as u16,
|
|
||||||
prdtl: prdt.len() as u16,
|
|
||||||
prdbc: 0,
|
|
||||||
ctba: buf_phys as u32,
|
|
||||||
ctbau: (buf_phys >> 32) as u32,
|
|
||||||
rsvd: [0; 4],
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
tbl_hdr_ptr.write_volatile(tbl_hdr);
|
|
||||||
prdt_ptr.copy_from_nonoverlapping(&prdt[0], prdt.len());
|
|
||||||
cmd_hdr_ptr.write_volatile(cmd_hdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.regs.PxCI.set(0x1);
|
|
||||||
|
|
||||||
while (self.regs.PxCI.get() & 0x1) > 0 && !self.has_fatal_error() {}
|
|
||||||
|
|
||||||
if self.has_fatal_error() {
|
|
||||||
self.regs.PxCMD.modify(PxCMD::ST::CLEAR);
|
|
||||||
while self.regs.PxCMD.read(PxCMD::CR) > 0 {}
|
|
||||||
Self::clear_serr(self.regs);
|
|
||||||
self.regs.PxIS.modify(PxIS::HBFS::SET);
|
|
||||||
self.regs.PxIS.modify(PxIS::HBDS::SET);
|
|
||||||
self.regs.PxIS.modify(PxIS::IFS::SET);
|
|
||||||
self.regs.PxIS.modify(PxIS::TFES::SET);
|
|
||||||
if self.regs.PxTFD.read(PxTFD::STS_BSY) > 0 || self.regs.PxTFD.read(PxTFD::STS_DRQ) > 0
|
|
||||||
{
|
|
||||||
unimplemented!("Port reset on error not implemented")
|
|
||||||
}
|
|
||||||
self.regs.PxCMD.modify(PxCMD::ST::SET);
|
|
||||||
return Err(CommandIssueError::CommandFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTIVE_SPACE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.unmap(buf, tbl_size.div_ceil(4096))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn issue_data_in_command<'a, T: AtaCommandDataIn>(
|
|
||||||
&self,
|
|
||||||
command: &T,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
) -> Result<T::ProcessedData<'a>, CommandIssueError> {
|
|
||||||
if buf.len() < command.data_len() {
|
|
||||||
return Err(CommandIssueError::DataTooSmall);
|
|
||||||
}
|
|
||||||
let len = buf.len();
|
|
||||||
|
|
||||||
if len > 0x40_0000 * 0xFFFF {
|
|
||||||
return Err(CommandIssueError::DataTooBig);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (data_buf, data_buf_phys) = ACTIVE_SPACE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.map_free_cont_phys(len.div_ceil(4096))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let num_prds = len.div_ceil(0x40_0000);
|
|
||||||
|
|
||||||
let prdt = (0..num_prds)
|
|
||||||
.map(|i| {
|
|
||||||
let size = if i == num_prds - 1 {
|
|
||||||
len - (i * 0x40_0000)
|
|
||||||
} else {
|
|
||||||
0x40_0000
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"Prd::new({:#x}, {:#x}, false)",
|
|
||||||
data_buf_phys + (i * 0x40_0000) as u64,
|
|
||||||
size as u32
|
|
||||||
);
|
|
||||||
Prd::new(data_buf_phys + (i * 0x40_0000) as u64, size as u32, false).unwrap()
|
|
||||||
})
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.issue_command_prdt(&command.get_fis(), &prdt)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data_vol = unsafe {
|
|
||||||
VolatilePtr::new(NonNull::slice_from_raw_parts(
|
|
||||||
NonNull::new(data_buf).unwrap(),
|
|
||||||
len,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
data_vol.copy_into_slice(buf);
|
|
||||||
|
|
||||||
ACTIVE_SPACE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.unmap(data_buf, len.div_ceil(4096))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(command.process_data(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_fatal_error(&self) -> bool {
|
|
||||||
self.regs.PxIS.read(PxIS::HBFS) > 0
|
|
||||||
|| self.regs.PxIS.read(PxIS::HBDS) > 0
|
|
||||||
|| self.regs.PxIS.read(PxIS::IFS) > 0
|
|
||||||
|| self.regs.PxIS.read(PxIS::TFES) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_serr(regs: &PortRegs) {
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_X::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_F::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_T::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_S::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_H::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_C::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_B::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_W::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_I::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::DIAG_N::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_E::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_P::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_C::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_T::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_M::SET);
|
|
||||||
regs.PxSERR.modify(PxSERR::ERR_I::SET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait AtaCommandDataIn {
|
|
||||||
type ProcessedData<'a>;
|
|
||||||
fn get_fis(&self) -> RegH2DFis;
|
|
||||||
fn data_len(&self) -> usize;
|
|
||||||
fn process_data<'a>(&self, data: &'a [u8]) -> Self::ProcessedData<'a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IdentifyCommand;
|
|
||||||
|
|
||||||
impl AtaCommandDataIn for IdentifyCommand {
|
|
||||||
type ProcessedData<'a> = IdentifyData;
|
|
||||||
|
|
||||||
fn get_fis(&self) -> RegH2DFis {
|
|
||||||
RegH2DFisBuilder::default()
|
|
||||||
.cmd(true)
|
|
||||||
.regs(
|
|
||||||
DeviceRegsWriteBuilder::default()
|
|
||||||
.commmad(0xEC)
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_len(&self) -> usize {
|
|
||||||
512
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_data(&self, data: &[u8]) -> Self::ProcessedData<'_> {
|
|
||||||
IdentifyData::read(&mut Cursor::new(data)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IdentifyPacketCommand;
|
|
||||||
|
|
||||||
impl AtaCommandDataIn for IdentifyPacketCommand {
|
|
||||||
type ProcessedData<'a> = IdentifyDataATAPI;
|
|
||||||
|
|
||||||
fn get_fis(&self) -> RegH2DFis {
|
|
||||||
RegH2DFisBuilder::default()
|
|
||||||
.cmd(true)
|
|
||||||
.regs(
|
|
||||||
DeviceRegsWriteBuilder::default()
|
|
||||||
.commmad(0xA1)
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_len(&self) -> usize {
|
|
||||||
512
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_data(&self, data: &[u8]) -> Self::ProcessedData<'_> {
|
|
||||||
IdentifyDataATAPI::read(&mut Cursor::new(data)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReadDmaExtCommand {
|
|
||||||
lba: u64,
|
|
||||||
count: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReadDmaExtCommand {
|
|
||||||
fn new(lba: u64, count: u16) -> Self {
|
|
||||||
Self { lba, count }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AtaCommandDataIn for ReadDmaExtCommand {
|
|
||||||
type ProcessedData<'a> = &'a [u8];
|
|
||||||
|
|
||||||
fn get_fis(&self) -> RegH2DFis {
|
|
||||||
let lba_bytes = self.lba.to_le_bytes();
|
|
||||||
|
|
||||||
RegH2DFisBuilder::default()
|
|
||||||
.cmd(true)
|
|
||||||
.regs(
|
|
||||||
DeviceRegsWriteBuilder::default()
|
|
||||||
.commmad(0x25)
|
|
||||||
.lba(true)
|
|
||||||
.countl(self.count as u8)
|
|
||||||
.counth((self.count >> 8) as u8)
|
|
||||||
.lba0(lba_bytes[0])
|
|
||||||
.lba1(lba_bytes[1])
|
|
||||||
.lba2(lba_bytes[2])
|
|
||||||
.lba3(lba_bytes[3])
|
|
||||||
.lba4(lba_bytes[4])
|
|
||||||
.lba5(lba_bytes[5])
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data_len(&self) -> usize {
|
|
||||||
(self.count * 512) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_data<'a>(&self, data: &'a [u8]) -> Self::ProcessedData<'a> {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let syslog_pid = loop {
|
let syslog_pid = loop {
|
||||||
if let Some(pid) = syscalls::try_get_registered(2) {
|
if let Some(pid) = syscalls::try_get_registered(2) {
|
||||||
@ -375,124 +79,13 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let ghc_regs = unsafe { &*(reg_base as *const GenHC) };
|
|
||||||
|
|
||||||
let supports_64bit_dma = ghc_regs.CAP.read(CAP::S64A) > 0;
|
let hba = unsafe { Hba::new(reg_base, syslog_client) }.unwrap();
|
||||||
if supports_64bit_dma {
|
|
||||||
syslog_client
|
|
||||||
.send_text_message("ahci", "HBA supports 64bit DMA".to_string())
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
syslog_client
|
|
||||||
.send_text_message("ahci", "HBA does not support 64bit DMA".to_string())
|
|
||||||
.unwrap();
|
|
||||||
syslog_client
|
|
||||||
.send_text_message(
|
|
||||||
"ahci",
|
|
||||||
"Aborting, there is no way to ensure buffers from the OS are in the low 4G."
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
syslog_client
|
for port in hba.ports() {
|
||||||
.send_text_message("ahci", "Resetting HBA".to_string())
|
if !port.has_device() {
|
||||||
.unwrap();
|
|
||||||
ghc_regs.GHC.modify(GHC::AE::SET);
|
|
||||||
ghc_regs.GHC.modify(GHC::HR::SET);
|
|
||||||
while ghc_regs.GHC.read(GHC::HR) > 0 {}
|
|
||||||
syslog_client
|
|
||||||
.send_text_message("ahci", "Reset HBA".to_string())
|
|
||||||
.unwrap();
|
|
||||||
ghc_regs.GHC.modify(GHC::AE::SET);
|
|
||||||
|
|
||||||
let num_raw_ports = (ghc_regs.CAP.read(CAP::NP) + 1) as usize;
|
|
||||||
let port_reg_base = unsafe { reg_base.add(0x100).cast::<PortRegs>() };
|
|
||||||
let num_ports = (ghc_regs.PI.get().count_ones()) as usize;
|
|
||||||
if num_ports == num_raw_ports {
|
|
||||||
syslog_client
|
|
||||||
.send_text_message("ahci", format!("HBA has {num_ports} ports"))
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
syslog_client
|
|
||||||
.send_text_message(
|
|
||||||
"ahci",
|
|
||||||
format!("HBA has {num_raw_ports} ports, but only {num_ports} are usable",),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let max_cmd_slots = (ghc_regs.CAP.read(CAP::NCS) + 1) as usize;
|
|
||||||
|
|
||||||
let fis_buf_mem = num_ports * 256;
|
|
||||||
let comm_list_mem_per_port = max_cmd_slots * 32;
|
|
||||||
let comm_list_mem = comm_list_mem_per_port * num_ports;
|
|
||||||
let total_mem = fis_buf_mem + comm_list_mem;
|
|
||||||
|
|
||||||
let (buf, buf_phys) = ACTIVE_SPACE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.map_free_cont_phys(total_mem.div_ceil(4096))
|
|
||||||
.unwrap();
|
|
||||||
unsafe {
|
|
||||||
buf.write_bytes(0, total_mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut avail_ports = Vec::new();
|
|
||||||
let avail_ports_raw = ghc_regs.PI.get();
|
|
||||||
|
|
||||||
for port_idx in 0..32 {
|
|
||||||
if (avail_ports_raw & (1 << port_idx)) == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let port = unsafe { &*(port_reg_base.add(port_idx)) };
|
|
||||||
let cmd_reg = &port.PxCMD;
|
|
||||||
if !(cmd_reg.read(PxCMD::ST) == 0
|
|
||||||
&& cmd_reg.read(PxCMD::CR) == 0
|
|
||||||
&& cmd_reg.read(PxCMD::FRE) == 0
|
|
||||||
&& cmd_reg.read(PxCMD::FR) == 0)
|
|
||||||
{
|
|
||||||
cmd_reg.modify(PxCMD::ST::CLEAR);
|
|
||||||
while cmd_reg.read(PxCMD::CR) > 0 {}
|
|
||||||
if cmd_reg.read(PxCMD::FRE) == 1 {
|
|
||||||
cmd_reg.modify(PxCMD::FRE::CLEAR);
|
|
||||||
while cmd_reg.read(PxCMD::FR) > 0 {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let cl_phys_ptr = buf_phys + (comm_list_mem_per_port * avail_ports.len()) as u64;
|
|
||||||
let fis_phys_ptr = buf_phys + (comm_list_mem + 256 * avail_ports.len()) as u64;
|
|
||||||
port.PxCLB.set(cl_phys_ptr as u32);
|
|
||||||
port.PxCLBU.set((cl_phys_ptr >> 32) as u32);
|
|
||||||
port.PxFB.set(fis_phys_ptr as u32);
|
|
||||||
port.PxFBU.set((fis_phys_ptr >> 32) as u32);
|
|
||||||
port.PxCMD.modify(PxCMD::FRE::SET);
|
|
||||||
AhciPort::clear_serr(port);
|
|
||||||
|
|
||||||
let has_device = port.PxTFD.read(PxTFD::STS_BSY) == 0
|
|
||||||
&& port.PxTFD.read(PxTFD::STS_DRQ) == 0
|
|
||||||
&& port.PxSSTS.read(PxSSTS::DET) == 3;
|
|
||||||
if has_device {
|
|
||||||
port.PxCMD.modify(PxCMD::ST::SET);
|
|
||||||
}
|
|
||||||
avail_ports.push(AhciPort {
|
|
||||||
regs: port,
|
|
||||||
phys_no: port_idx,
|
|
||||||
fis_buf: unsafe { FisBuf::new(buf.add(comm_list_mem + 256 * avail_ports.len())) },
|
|
||||||
cmd_list: unsafe {
|
|
||||||
buf.add(comm_list_mem_per_port * avail_ports.len())
|
|
||||||
.cast::<CommandHeader>()
|
|
||||||
},
|
|
||||||
cmd_list_len: max_cmd_slots,
|
|
||||||
has_device,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for port in &avail_ports {
|
|
||||||
if !port.has_device {
|
|
||||||
syslog_client
|
syslog_client
|
||||||
.send_text_message("ahci", format!("Port {}: Empty", port.phys_no))
|
.send_text_message("ahci", format!("Port {}: Empty", port.phys_no()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -522,7 +115,7 @@ fn main() {
|
|||||||
"ahci",
|
"ahci",
|
||||||
format!(
|
format!(
|
||||||
"Port {}: {} {}, firmware {} ({})",
|
"Port {}: {} {}, firmware {} ({})",
|
||||||
port.phys_no,
|
port.phys_no(),
|
||||||
identify_data.model,
|
identify_data.model,
|
||||||
identify_data.serial,
|
identify_data.serial,
|
||||||
identify_data.firmware,
|
identify_data.firmware,
|
||||||
@ -531,19 +124,26 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
let reg_fis = port.fis_buf.get_d2h_reg_fis().unwrap();
|
let regs = port.regs().unwrap();
|
||||||
|
|
||||||
if reg_fis.regs.lba1 != 0x14 || reg_fis.regs.lba2 != 0xEB {
|
if regs.lba1 != 0x14 || regs.lba2 != 0xEB {
|
||||||
syslog_client
|
syslog_client
|
||||||
.send_text_message("ahci", format!("Port {} failed to identify", port.phys_no))
|
.send_text_message(
|
||||||
|
"ahci",
|
||||||
|
format!("Port {} failed to identify", port.phys_no()),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(identify_data) = port.issue_data_in_command(&IdentifyPacketCommand, &mut ident_buf)
|
let Ok(identify_data) =
|
||||||
|
port.issue_data_in_command(&IdentifyPacketCommand, &mut ident_buf)
|
||||||
else {
|
else {
|
||||||
syslog_client
|
syslog_client
|
||||||
.send_text_message("ahci", format!("Port {} failed to identify", port.phys_no))
|
.send_text_message(
|
||||||
|
"ahci",
|
||||||
|
format!("Port {} failed to identify", port.phys_no()),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -553,7 +153,7 @@ fn main() {
|
|||||||
"ahci",
|
"ahci",
|
||||||
format!(
|
format!(
|
||||||
"Port {}: {} {}, firmware {}",
|
"Port {}: {} {}, firmware {}",
|
||||||
port.phys_no,
|
port.phys_no(),
|
||||||
identify_data.model,
|
identify_data.model,
|
||||||
identify_data.serial,
|
identify_data.serial,
|
||||||
identify_data.firmware
|
identify_data.firmware
|
||||||
@ -565,7 +165,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut mbr = [0; 512];
|
let mut mbr = [0; 512];
|
||||||
|
|
||||||
avail_ports[0]
|
hba.ports()[0]
|
||||||
.issue_data_in_command(&ReadDmaExtCommand::new(0, 1), &mut mbr)
|
.issue_data_in_command(&ReadDmaExtCommand::new(0, 1), &mut mbr)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -606,7 +206,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut gpt_header = [0; 512];
|
let mut gpt_header = [0; 512];
|
||||||
|
|
||||||
avail_ports[0]
|
hba.ports()[0]
|
||||||
.issue_data_in_command(&ReadDmaExtCommand::new(1, 1), &mut gpt_header)
|
.issue_data_in_command(&ReadDmaExtCommand::new(1, 1), &mut gpt_header)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -641,7 +241,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut gpt_part_table = vec![0; part_table_num_lbas * 512];
|
let mut gpt_part_table = vec![0; part_table_num_lbas * 512];
|
||||||
|
|
||||||
avail_ports[0]
|
hba.ports()[0]
|
||||||
.issue_data_in_command(
|
.issue_data_in_command(
|
||||||
&ReadDmaExtCommand::new(part_table_start_lba as u64, part_table_num_lbas as u16),
|
&ReadDmaExtCommand::new(part_table_start_lba as u64, part_table_num_lbas as u16),
|
||||||
gpt_part_table.as_mut_slice(),
|
gpt_part_table.as_mut_slice(),
|
||||||
|
253
src/port.rs
Normal file
253
src/port.rs
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
use std::{os::mikros::address_space::ACTIVE_SPACE, ptr::NonNull};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use volatile::VolatilePtr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ahci_structs::{
|
||||||
|
CommandHeader, CommandTableHeader, DeviceRegsRead, FisBuf, PortRegs, Prd, PxCMD, PxIS,
|
||||||
|
PxSERR, PxSSTS, PxTFD, RegH2DFis,
|
||||||
|
},
|
||||||
|
ata_command::AtaCommandDataIn,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
pub struct AhciPort {
|
||||||
|
regs: &'static PortRegs,
|
||||||
|
phys_no: usize,
|
||||||
|
fis_buf: FisBuf,
|
||||||
|
cmd_list: *mut CommandHeader,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
cmd_list_len: usize,
|
||||||
|
has_device: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum CommandIssueError {
|
||||||
|
PrdtTooBig,
|
||||||
|
DataTooBig,
|
||||||
|
CommandFailed,
|
||||||
|
DataTooSmall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AhciPort {
|
||||||
|
pub fn new(
|
||||||
|
regs: &'static PortRegs,
|
||||||
|
phys_no: usize,
|
||||||
|
fis_buf: FisBuf,
|
||||||
|
fis_buf_phys: u64,
|
||||||
|
cmd_list: *mut CommandHeader,
|
||||||
|
cmd_list_phys: u64,
|
||||||
|
cmd_list_len: usize,
|
||||||
|
) -> Self {
|
||||||
|
let cmd_reg = ®s.PxCMD;
|
||||||
|
if !(cmd_reg.read(PxCMD::ST) == 0
|
||||||
|
&& cmd_reg.read(PxCMD::CR) == 0
|
||||||
|
&& cmd_reg.read(PxCMD::FRE) == 0
|
||||||
|
&& cmd_reg.read(PxCMD::FR) == 0)
|
||||||
|
{
|
||||||
|
cmd_reg.modify(PxCMD::ST::CLEAR);
|
||||||
|
while cmd_reg.read(PxCMD::CR) > 0 {}
|
||||||
|
if cmd_reg.read(PxCMD::FRE) == 1 {
|
||||||
|
cmd_reg.modify(PxCMD::FRE::CLEAR);
|
||||||
|
while cmd_reg.read(PxCMD::FR) > 0 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regs.PxCLB.set(cmd_list_phys as u32);
|
||||||
|
regs.PxCLBU.set((cmd_list_phys >> 32) as u32);
|
||||||
|
regs.PxFB.set(fis_buf_phys as u32);
|
||||||
|
regs.PxFBU.set((fis_buf_phys >> 32) as u32);
|
||||||
|
regs.PxCMD.modify(PxCMD::FRE::SET);
|
||||||
|
Self::clear_serr(regs);
|
||||||
|
|
||||||
|
let has_device = regs.PxTFD.read(PxTFD::STS_BSY) == 0
|
||||||
|
&& regs.PxTFD.read(PxTFD::STS_DRQ) == 0
|
||||||
|
&& regs.PxSSTS.read(PxSSTS::DET) == 3;
|
||||||
|
if has_device {
|
||||||
|
regs.PxCMD.modify(PxCMD::ST::SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
regs,
|
||||||
|
phys_no,
|
||||||
|
fis_buf,
|
||||||
|
cmd_list,
|
||||||
|
cmd_list_len,
|
||||||
|
has_device,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn issue_command_prdt(
|
||||||
|
&self,
|
||||||
|
fis: &RegH2DFis,
|
||||||
|
prdt: &[Prd],
|
||||||
|
) -> Result<(), CommandIssueError> {
|
||||||
|
if prdt.len() > 65535 {
|
||||||
|
return Err(CommandIssueError::PrdtTooBig);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tbl_hdr = CommandTableHeader {
|
||||||
|
cfis: [0; 64],
|
||||||
|
acmd: [0; 16],
|
||||||
|
rsvd: [0; 48],
|
||||||
|
};
|
||||||
|
|
||||||
|
tbl_hdr.cfis[0..RegH2DFis::BYTE_SIZE].copy_from_slice(&fis.to_bytes());
|
||||||
|
|
||||||
|
let tbl_size = std::mem::size_of::<CommandTableHeader>() + std::mem::size_of_val(prdt);
|
||||||
|
|
||||||
|
let (buf, buf_phys) = ACTIVE_SPACE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.map_free_cont_phys(tbl_size.div_ceil(4096))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tbl_hdr_ptr = buf.cast::<CommandTableHeader>();
|
||||||
|
let prdt_ptr = unsafe {
|
||||||
|
buf.add(std::mem::size_of::<CommandTableHeader>())
|
||||||
|
.cast::<Prd>()
|
||||||
|
};
|
||||||
|
let cmd_hdr_ptr = self.cmd_list;
|
||||||
|
|
||||||
|
let cmd_hdr = CommandHeader {
|
||||||
|
flags: (RegH2DFis::BYTE_SIZE / 4) as u16,
|
||||||
|
prdtl: prdt.len() as u16,
|
||||||
|
prdbc: 0,
|
||||||
|
ctba: buf_phys as u32,
|
||||||
|
ctbau: (buf_phys >> 32) as u32,
|
||||||
|
rsvd: [0; 4],
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
tbl_hdr_ptr.write_volatile(tbl_hdr);
|
||||||
|
prdt_ptr.copy_from_nonoverlapping(&prdt[0], prdt.len());
|
||||||
|
cmd_hdr_ptr.write_volatile(cmd_hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.regs.PxCI.set(0x1);
|
||||||
|
|
||||||
|
while (self.regs.PxCI.get() & 0x1) > 0 && !self.has_fatal_error() {}
|
||||||
|
|
||||||
|
if self.has_fatal_error() {
|
||||||
|
self.regs.PxCMD.modify(PxCMD::ST::CLEAR);
|
||||||
|
while self.regs.PxCMD.read(PxCMD::CR) > 0 {}
|
||||||
|
Self::clear_serr(self.regs);
|
||||||
|
self.regs.PxIS.modify(PxIS::HBFS::SET);
|
||||||
|
self.regs.PxIS.modify(PxIS::HBDS::SET);
|
||||||
|
self.regs.PxIS.modify(PxIS::IFS::SET);
|
||||||
|
self.regs.PxIS.modify(PxIS::TFES::SET);
|
||||||
|
if self.regs.PxTFD.read(PxTFD::STS_BSY) > 0 || self.regs.PxTFD.read(PxTFD::STS_DRQ) > 0
|
||||||
|
{
|
||||||
|
unimplemented!("Port reset on error not implemented")
|
||||||
|
}
|
||||||
|
self.regs.PxCMD.modify(PxCMD::ST::SET);
|
||||||
|
return Err(CommandIssueError::CommandFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
ACTIVE_SPACE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.unmap(buf, tbl_size.div_ceil(4096))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn issue_data_in_command<'a, T: AtaCommandDataIn>(
|
||||||
|
&self,
|
||||||
|
command: &T,
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
) -> Result<T::ProcessedData<'a>, CommandIssueError> {
|
||||||
|
if buf.len() < command.data_len() {
|
||||||
|
return Err(CommandIssueError::DataTooSmall);
|
||||||
|
}
|
||||||
|
let len = buf.len();
|
||||||
|
|
||||||
|
if len > 0x40_0000 * 0xFFFF {
|
||||||
|
return Err(CommandIssueError::DataTooBig);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (data_buf, data_buf_phys) = ACTIVE_SPACE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.map_free_cont_phys(len.div_ceil(4096))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let num_prds = len.div_ceil(0x40_0000);
|
||||||
|
|
||||||
|
let prdt = (0..num_prds)
|
||||||
|
.map(|i| {
|
||||||
|
let size = if i == num_prds - 1 {
|
||||||
|
len - (i * 0x40_0000)
|
||||||
|
} else {
|
||||||
|
0x40_0000
|
||||||
|
};
|
||||||
|
println!(
|
||||||
|
"Prd::new({:#x}, {:#x}, false)",
|
||||||
|
data_buf_phys + (i * 0x40_0000) as u64,
|
||||||
|
size as u32
|
||||||
|
);
|
||||||
|
Prd::new(data_buf_phys + (i * 0x40_0000) as u64, size as u32, false).unwrap()
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.issue_command_prdt(&command.get_fis(), &prdt)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_vol = unsafe {
|
||||||
|
VolatilePtr::new(NonNull::slice_from_raw_parts(
|
||||||
|
NonNull::new(data_buf).unwrap(),
|
||||||
|
len,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
data_vol.copy_into_slice(buf);
|
||||||
|
|
||||||
|
ACTIVE_SPACE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.unmap(data_buf, len.div_ceil(4096))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(command.process_data(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_fatal_error(&self) -> bool {
|
||||||
|
self.regs.PxIS.read(PxIS::HBFS) > 0
|
||||||
|
|| self.regs.PxIS.read(PxIS::HBDS) > 0
|
||||||
|
|| self.regs.PxIS.read(PxIS::IFS) > 0
|
||||||
|
|| self.regs.PxIS.read(PxIS::TFES) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_serr(regs: &PortRegs) {
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_X::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_F::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_T::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_S::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_H::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_C::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_B::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_W::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_I::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::DIAG_N::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_E::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_P::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_C::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_T::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_M::SET);
|
||||||
|
regs.PxSERR.modify(PxSERR::ERR_I::SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phys_no(&self) -> usize {
|
||||||
|
self.phys_no
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_device(&self) -> bool {
|
||||||
|
self.has_device
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn regs(&self) -> Option<DeviceRegsRead> {
|
||||||
|
self.fis_buf.get_d2h_reg_fis().map(|x| x.regs)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user