From f140938ee6609502c043e42840ff2a83b82d4ab8 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Tue, 16 Aug 2022 16:35:34 +0300 Subject: [PATCH] working IDE DMA implementation --- Cargo.lock | 7 + ableos/Cargo.toml | 2 +- ableos/src/arch/x86_64/interrupts.rs | 32 +++- ableos/src/devices/pci/device.rs | 52 ++++-- ableos/src/devices/pci/mod.rs | 26 ++- .../src/devices/pci/{piix.rs => piix_ide.rs} | 149 +++++++++++++++--- ableos/src/scratchpad.rs | 19 +++ 7 files changed, 245 insertions(+), 42 deletions(-) rename ableos/src/devices/pci/{piix.rs => piix_ide.rs} (76%) diff --git a/Cargo.lock b/Cargo.lock index a19f7b10..3a0df9f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/ableos/Cargo.toml b/ableos/Cargo.toml index f8b2669a..5c0639f4 100644 --- a/ableos/Cargo.toml +++ b/ableos/Cargo.toml @@ -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" diff --git a/ableos/src/arch/x86_64/interrupts.rs b/ableos/src/arch/x86_64/interrupts.rs index a59384af..23700114 100644 --- a/ableos/src/arch/x86_64/interrupts.rs +++ b/ableos/src/arch/x86_64/interrupts.rs @@ -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 = 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 = 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) { diff --git a/ableos/src/devices/pci/device.rs b/ableos/src/devices/pci/device.rs index 922a241b..217c6d31 100644 --- a/ableos/src/devices/pci/device.rs +++ b/ableos/src/devices/pci/device.rs @@ -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 { 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 { }) } -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::::new(0xCF8).write(address); + Port::new(0xCF8).write(address); // read data - Port::::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) diff --git a/ableos/src/devices/pci/mod.rs b/ableos/src/devices/pci/mod.rs index f14a264b..5e301cf7 100644 --- a/ableos/src/devices/pci/mod.rs +++ b/ableos/src/devices/pci/mod.rs @@ -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>>> = 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, 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) diff --git a/ableos/src/devices/pci/piix.rs b/ableos/src/devices/pci/piix_ide.rs similarity index 76% rename from ableos/src/devices/pci/piix.rs rename to ableos/src/devices/pci/piix_ide.rs index d1bda613..cbe03c01 100644 --- a/ableos/src/devices/pci/piix.rs +++ b/ableos/src/devices/pci/piix_ide.rs @@ -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, prdt_frame: Option, buffer_frames: Option>, bmiba: u16, } -impl Piix { +impl PiixIde { // FIXME: make this return a Result pub fn new(bus: u8, device: u8) -> Option { 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 = 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 = 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 = 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)] diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index bfb77519..2b5e71a9 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -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(); }