working IDE DMA implementation

This commit is contained in:
TheOddGarlic 2022-08-16 16:35:34 +03:00
parent 998a1d30cf
commit f140938ee6
7 changed files with 245 additions and 42 deletions

7
Cargo.lock generated
View file

@ -54,6 +54,7 @@ dependencies = [
"rdrand",
"riscv",
"rkyv",
"seq-macro",
"serde",
"spin 0.9.4",
"toml",
@ -648,6 +649,12 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "seq-macro"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0772c5c30e1a0d91f6834f8e545c69281c099dfa9a3ac58d96a9fd629c8d4898"
[[package]]
name = "serde"
version = "1.0.141"

View file

@ -78,7 +78,7 @@ versioning = { git = "https://git.ablecorp.us/able/aos_userland" }
pc-keyboard = "0.5"
# mini-backtrace = "0.1"
clparse = { git = "https://git.ablecorp.us/able/core_utils", default-features = false }
seq-macro = "0.3"
[dependencies.linked_list_allocator]
version = "0.9.0"

View file

@ -6,10 +6,16 @@
use core::panic::PanicInfo;
use crate::{arch::gdt, println, rhai_shell::KEYBUFF};
use crate::{
arch::gdt,
devices::pci::{PciDevice, PCI_DEVICES},
println,
rhai_shell::KEYBUFF,
};
use cpuio::outb;
use pic8259::ChainedPics;
use qrcode::QrCode;
use seq_macro::seq;
use spin::Lazy;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
@ -30,6 +36,9 @@ pub enum InterruptIndex {
/// Mouse offset
Mouse = 44,
/// Disk offset
Disk = 46,
// SecondInterrupt = PIC_2_OFFSET,
Cmos = 0x70,
}
@ -47,9 +56,9 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
reset_pit_for_cpu();
let mut idt = InterruptDescriptorTable::new();
for int in 32..=255 {
idt[int].set_handler_fn(undefined_handler);
}
seq!(N in 32..=255 {
idt[N].set_handler_fn(undefined_handler_~N);
});
idt.breakpoint.set_handler_fn(breakpoint_handler);
unsafe {
@ -68,12 +77,21 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
idt
});
extern "x86-interrupt" fn undefined_handler(stack_frame: InterruptStackFrame) {
error!("{:?}", stack_frame);
}
seq!(N in 32..=255 {
extern "x86-interrupt" fn undefined_handler_~N(stack_frame: InterruptStackFrame) {
error!("INT {}: {:?}", N, stack_frame);
unsafe {
PICS.lock()
.notify_end_of_interrupt(N);
}
}
});
extern "x86-interrupt" fn software_int_handler(stack_frame: InterruptStackFrame) {
trace!("EXCEPTION: SOFTWARE INT\n{:#?}", stack_frame);
unsafe {
PICS.lock().notify_end_of_interrupt(54);
}
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {

View file

@ -7,6 +7,7 @@
use core::fmt;
use serde::de::value;
use x86_64::instructions::port::Port;
use super::{
@ -24,7 +25,7 @@ pub const INTEL_PIIX4_IDE: DeviceID = DeviceID::new(IntelCorp, 0x7111);
// Display_VGA (0x0300)
pub const VMWARE_SVGA2: DeviceID = DeviceID::new(VMWareInc, 0x0405);
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
/// A struct containing info about a PCI device.
pub struct PciDeviceInfo {
pub header_type: u8,
@ -44,18 +45,29 @@ impl PciDeviceInfo {
/// Get the bar, 0-indexed
pub fn bar(&self, bar: u8) -> u32 {
assert!(bar < 6);
unsafe { self.io_read(0, 0x10 + bar * 4) }
unsafe { self.read(0, 0x10 + bar * 4) }
}
/// Get the interrupt pin
pub fn interrupt_pin(&self) -> u8 {
let last_row = unsafe { self.io_read(0, 0x3C) };
let last_row = unsafe { self.read(0, 0x3C) };
((last_row >> 8) & 0xFF) as u8
}
/// Read from IO space
pub unsafe fn io_read(&self, func: u8, offset: u8) -> u32 {
pci_io_read(self.bus, self.device, func, offset)
/// Enable bus mastering. This allows the PCI device to do DMA
pub fn enable_bus_mastering(&self) {
let command = unsafe { self.read(0, 4) } | 1 << 2;
unsafe { self.write(0, 4, command) }
}
/// Read from configuration space
pub unsafe fn read(&self, func: u8, offset: u8) -> u32 {
pci_config_read(self.bus, self.device, func, offset)
}
/// Write to IO space
pub unsafe fn write(&self, func: u8, offset: u8, value: u32) {
pci_config_write(self.bus, self.device, func, offset, value)
}
}
@ -116,7 +128,7 @@ pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
return None;
}
let reg2 = unsafe { pci_io_read(bus, device, 0, 0x8) };
let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) };
let class = ((reg2 >> 16) & 0x0000FFFF) as u16;
let pci_class = PciFullClass::from_u16(class);
let header_type = get_header_type(bus, device, 0);
@ -134,7 +146,7 @@ pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
})
}
unsafe fn pci_io_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
@ -144,23 +156,39 @@ unsafe fn pci_io_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
Port::<u32>::new(0xCF8).write(address);
Port::new(0xCF8).write(address);
// read data
Port::<u32>::new(0xCFC).read()
Port::new(0xCFC).read()
}
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
let offset = offset as u32;
// construct address param
let address =
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
Port::new(0xCF8).write(address);
// write data
Port::new(0xCFC).write(value);
}
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_io_read(bus, device, function, 0x0C) };
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
((res >> 16) & 0xFF) as u8
}
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_io_read(bus, device, function, 0) };
let res = unsafe { pci_config_read(bus, device, function, 0) };
let dev_id = ((res >> 16) & 0xFFFF) as u16;
let vnd_id = (res & 0xFFFF) as u16;
(dev_id, vnd_id)

View file

@ -9,30 +9,48 @@ pub mod device;
pub mod vendors;
// MassStorage_IDE (0x0101)
pub mod piix;
pub mod piix_ide;
use alloc::sync::Arc;
pub use class::*;
pub use device::*;
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::structures::paging::{Mapper, Size4KiB};
use crate::arch::memory::BootInfoFrameAllocator;
use self::piix::Piix;
use self::piix_ide::PiixIde;
lazy_static! {
pub static ref PCI_DEVICES: Mutex<Vec<Arc<Mutex<PciDevice>>>> = Default::default();
}
#[non_exhaustive]
pub enum PciDevice {
// MassStorage_IDE (0x0101)
PiixIde(PiixIde),
// Variant so that we aren't about irrefutable if-let patterns
// FIXME: remove as soon as we have other variants
_0,
}
/// Enumerate PCI devices and run initialisation routines on ones we support
pub fn init(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut BootInfoFrameAllocator) {
for bus in 0..=255 {
for device in 0..32 {
if let Some(device_info) = device::check_device(bus, device) {
trace!("{device_info}");
match device_info.device_id {
// FIXME: Unknown class
S3INC_TRIO64V2 => {}
// MassStorage_IDE (0x0101)
INTEL_PIIX3_IDE | INTEL_PIIX4_IDE => {
let mut piix = Piix::new(bus, device).unwrap();
let mut piix = PiixIde::new(bus, device).unwrap();
piix.allocate_dma_frame(mapper, frame_allocator).unwrap();
piix.read().unwrap();
let mut devices = PCI_DEVICES.lock();
devices.push(Arc::new(Mutex::new(PciDevice::PiixIde(piix))));
}
// Display_VGA (0x0300)

View file

@ -4,10 +4,11 @@
* SPDX-License-Identifier: MPL-2.0
*/
use core::mem;
use core::num::TryFromIntError;
use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;
use x86_64::instructions::{hlt, interrupts};
use x86_64::structures::paging::{FrameAllocator, FrameDeallocator};
// FIXME: platform agnostic paging stuff
use x86_64::structures::paging::{mapper::MapToError, Mapper, Page, PhysFrame, Size4KiB};
@ -16,8 +17,11 @@ use x86_64::VirtAddr;
use crate::arch::memory::BootInfoFrameAllocator;
use crate::devices::pci::check_device;
const PRDT_START: u64 = 0x_ffff_ffff_0000_0000;
const BUFFER_START: u64 = 0x_ffff_ffff_0000_1000;
use super::PciDeviceInfo;
// FIXME: un-hardcode these
const PRDT_START: u64 = 0xffff_ffff_0000_0000;
const BUFFER_START: u64 = 0xffff_ffff_0000_1000;
/// Bus Master IDE Command
const BMIC_OFFSET: u16 = 0;
@ -38,6 +42,18 @@ const SECONDARY_COMMAND: u16 = 0x0170;
/// Data register offset
const DATA_OFFSET: u16 = 0;
/// Sector count register offset
const SECCOUNT_OFFSET: u16 = 2;
/// LBA0 register offset
const LBA0_OFFSET: u16 = 3;
/// LBA1 register offset
const LBA1_OFFSET: u16 = 4;
/// LBA2 register offset
const LBA2_OFFSET: u16 = 5;
/// Drive/Head register offset
const DRIVE_HEAD_OFFSET: u16 = 6;
@ -56,19 +72,23 @@ const ALT_STATUS_OFFSET: u16 = 2;
/// ATA identification command
const CMD_IDENTIFY: u8 = 0xEC;
pub struct Piix {
/// ATA read using LBA48 DMA command
const CMD_READ_DMA_EXT: u8 = 0x25;
pub struct PiixIde {
device_info: PciDeviceInfo,
ide_devices: Vec<IdeDevice>,
prdt_frame: Option<PhysFrame>,
buffer_frames: Option<Vec<PhysFrame>>,
bmiba: u16,
}
impl Piix {
impl PiixIde {
// FIXME: make this return a Result
pub fn new(bus: u8, device: u8) -> Option<Self> {
let device_info = check_device(bus, device)?;
trace!("device_info: {device_info}");
let idetim = unsafe { device_info.io_read(0, 0x40) };
device_info.enable_bus_mastering();
let idetim = unsafe { device_info.read(0, 0x40) };
trace!("idetim: {idetim:b}");
// FIXME: enable the right bits in idetim (and sidetim) to use fast timings
@ -160,6 +180,7 @@ impl Piix {
let bmiba = device_info.bar(4) & 0xFFFFFFFC;
Some(Self {
device_info,
ide_devices,
prdt_frame: None,
buffer_frames: None,
@ -177,6 +198,7 @@ impl Piix {
let prdt_frame = frame_allocator
.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
let buffer_frames = {
let mut frame = frame_allocator
.allocate_frame()
@ -231,7 +253,7 @@ impl Piix {
Ok(())
}
pub fn read(&self) -> Result<(), TryFromIntError> {
pub fn read(&mut self) -> Result<(), TryFromIntError> {
// prepare PRD table
let prd = PRDT_START as *mut PhysRegionDescriptor;
unsafe {
@ -243,19 +265,51 @@ impl Piix {
(*prd).byte_count = 512;
// this is the end of table
(*prd).eot = 1 << 7;
// this byte is reserved, we should probably set it to 0
(*prd)._0 = 0;
}
unsafe {
self.load_prdt(Channel::Primary);
self.stop(Channel::Primary);
self.set_read(Channel::Primary);
self.clear_status(Channel::Primary);
self.clear_bmi_status(Channel::Primary);
select_drive(Drive::Master, Channel::Primary);
set_lba(Channel::Primary, 0, 1);
ata_send_command(CMD_READ_DMA_EXT, Channel::Primary);
self.start(Channel::Primary);
loop {
let status = self.bmi_status(Channel::Primary);
trace!("read status: 0b{status:b}");
// Bit 2 (INT) set?
if (status >> 2) & 1 == 1 {
break;
}
}
// FIXME: error handling
// Stop DMA
self.stop(Channel::Primary);
// Clear the interrupt bit
self.clear_bmi_status(Channel::Primary);
for i in 0..512 {
let addr = (BUFFER_START + i) as *mut u8;
trace!("byte {i}: {}", *addr);
}
}
Ok(())
}
pub fn device_info(&self) -> PciDeviceInfo {
self.device_info
}
unsafe fn load_prdt(&self, channel: Channel) {
let addr = if channel.secondary() {
BMI_SECONDARY
@ -273,6 +327,21 @@ impl Piix {
);
}
unsafe fn start(&self, channel: Channel) {
let addr = if channel.secondary() {
BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIC_OFFSET;
let mut port: Port<u8> = Port::new(addr);
let mut bmic = port.read();
// start transfer
bmic |= 1;
// write the new bmic
port.write(bmic);
}
unsafe fn stop(&self, channel: Channel) {
let addr = if channel.secondary() {
BMI_SECONDARY
@ -318,7 +387,18 @@ impl Piix {
port.write(bmic);
}
unsafe fn clear_status(&self, channel: Channel) {
unsafe fn bmi_status(&self, channel: Channel) -> u8 {
let addr = if channel.secondary() {
BMI_SECONDARY
} else {
0
} + self.bmiba
+ BMIS_OFFSET;
let mut port = Port::new(addr);
port.read()
}
unsafe fn clear_bmi_status(&self, channel: Channel) {
let addr = if channel.secondary() {
BMI_SECONDARY
} else {
@ -341,14 +421,15 @@ unsafe fn select_drive(drive: Drive, channel: Channel) {
PRIMARY_COMMAND
} + DRIVE_HEAD_OFFSET;
let mut port: Port<u8> = Port::new(addr);
let mut drive_command = port.read();
if drive.slave() {
// mark bit 4
drive_command |= 1 << 4;
// FIXME: CHS support
let drive_command = if drive.slave() {
// slave & LBA
0b11110000
} else {
// clear bit 4
drive_command &= !(1 << 4);
}
// master & LBA
0b11100000
};
// write the new drive/head register
port.write(drive_command);
ata_delay(channel);
@ -361,7 +442,7 @@ unsafe fn ata_send_command(command: u8, channel: Channel) -> u8 {
} else {
PRIMARY_COMMAND
} + COMMAND_STATUS_OFFSET;
let mut port: Port<u8> = Port::new(addr);
let mut port = Port::new(addr);
port.write(command);
ata_delay(channel);
port.read()
@ -380,6 +461,37 @@ unsafe fn ata_delay(channel: Channel) {
}
}
/// Set LBA and sector count registers. sector_count of 0 means 65536 sectors
unsafe fn set_lba(channel: Channel, lba: u64, sector_count: u16) {
// FIXME: CHS and LBA24 support
assert!(lba < 0xFFFFFFFFFFFF);
let command_block = if channel.secondary() {
SECONDARY_COMMAND
} else {
PRIMARY_COMMAND
};
let mut seccount = Port::new(command_block + SECCOUNT_OFFSET);
let mut lba0 = Port::new(command_block + LBA0_OFFSET);
let mut lba1 = Port::new(command_block + LBA1_OFFSET);
let mut lba2 = Port::new(command_block + LBA2_OFFSET);
let lba_bytes = lba.to_le_bytes();
let sector_count_bytes = sector_count.to_le_bytes();
// write the new LBA & sector count registers
// if LBA48 {
seccount.write(sector_count_bytes[1]);
lba0.write(lba_bytes[3]);
lba1.write(lba_bytes[4]);
lba2.write(lba_bytes[5]);
// }
seccount.write(sector_count_bytes[0]);
lba0.write(lba_bytes[0]);
lba1.write(lba_bytes[1]);
lba2.write(lba_bytes[2]);
}
unsafe fn read_dword_buffer(port: u16, buffer: *mut u32, mut count: u32) {
// FIXME: this assumes x86-64
interrupts::without_interrupts(|| {
@ -398,6 +510,7 @@ struct IdeDevice {
pub channel: Channel,
pub drive: Drive,
pub size: u64, // in sectors
// FIXME: model
}
#[derive(Copy, Clone, Debug)]

View file

@ -6,6 +6,7 @@
use crate::arch::drivers::sysinfo::master;
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
use crate::devices::pci::{PciDevice, PCI_DEVICES};
use crate::filesystem;
use crate::filesystem::vfs::VFS;
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
@ -112,6 +113,24 @@ pub fn scratchpad() {
BANNER_WIDTH
);
let piix_ide_device = {
let pci_devices = PCI_DEVICES.lock();
pci_devices
.iter()
.find_map(|device_ref| {
let device = device_ref.lock();
if let PciDevice::PiixIde(_) = &*device {
Some(device_ref.clone())
} else {
None
}
})
.unwrap()
};
let mut piix_ide_device = piix_ide_device.lock();
if let PciDevice::PiixIde(device) = &mut *piix_ide_device {
device.read().unwrap()
}
real_shell();
}