diff --git a/os/kernel/src/boot.rs b/os/kernel/src/boot.rs index 370ea45..d902b12 100644 --- a/os/kernel/src/boot.rs +++ b/os/kernel/src/boot.rs @@ -203,6 +203,30 @@ pub extern "C" fn start(multiboot2_magic: u32, multiboot2_addr: *const BootInfor // Initialize network stack network::init(); + let drive = storage::block_device("ata0").unwrap(); + let mut buffer = [0; 8192]; + + // Fill buffer with some data + for i in 0..8192 { + buffer[i] = i as u8; + } + // Write data to the third sector of the drive + drive.write(3, 16, &mut buffer); + + // Fill buffer with zeroes + for i in 0..8192 { + buffer[i] = 0; + } + // Read data from the third sector of the drive + drive.read(3, 16, &mut buffer); + + // Check integrity of read data + for i in 0..8192 { + if buffer[i] != (i as u8) { + panic!("Data integrity check failed!"); + } + } + // Set up network interface for emulated QEMU network (IP: 10.0.2.15, Gateway: 10.0.2.2) if let Some(rtl8139) = rtl8139() && qemu_cfg::is_available() { let time = timer.systime_ms(); diff --git a/os/kernel/src/device/ide.rs b/os/kernel/src/device/ide.rs index 92d8c38..922f901 100644 --- a/os/kernel/src/device/ide.rs +++ b/os/kernel/src/device/ide.rs @@ -1,3 +1,13 @@ +/* ╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ Module: ide ║ + ╟──────────────────────────────────────────────────────────────────────────────────────────────────╢ + ║ Descr.: The IDE driver is based on a bachelor's thesis, written by Tim Laurischkat. ║ + ║ he original source code can be found here: https://git.hhu.de/bsinfo/thesis/ba-tilau101 ║ + ╟──────────────────────────────────────────────────────────────────────────────────────────────────╢ + ║ Author: Tim Laurischkat & Fabian Ruhland, HHU ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝ +*/ + use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; @@ -12,8 +22,32 @@ use crate::{apic, interrupt_dispatcher, memory, pci_bus, scheduler, timer}; use crate::interrupt::interrupt_dispatcher::InterruptVector; use crate::interrupt::interrupt_handler::InterruptHandler; use crate::memory::PAGE_SIZE; -use crate::storage::lba_to_chs; +use crate::storage::{add_block_device, lba_to_chs, BlockDevice}; + +/// Initialize all IDE controllers found on the PCI bus. +/// Each connected drive gets registered as a block device in the storage module. +pub fn init() { + let devices = pci_bus().search_by_class(0x01, 0x01); + for device in devices { + let device_id = device.read().header().id(&pci_bus().config_space()); + info!("Found IDE controller [{}:{}]", device_id.0, device_id.1); + + let ide_controller = Arc::new(IdeController::new(device)); + let found_drives = ide_controller.init_drives(); + + for drive in found_drives.iter() { + let block_device = Arc::new(IdeDrive::new(Arc::clone(&ide_controller), *drive)); + add_block_device("ata", block_device); + } + + IdeController::plugin(Arc::clone(&ide_controller)); + } +} +/* ╔═════════════════════════════════════════════════════════════════════════╗ + ║ Constants needed for the driver. ║ + ╚═════════════════════════════════════════════════════════════════════════╝ +*/ const CHANNELS_PER_CONTROLLER: u8 = 2; const DEVICES_PER_CHANNEL: u8 = 2; const DEFAULT_BASE_ADDRESSES: [u16; DEVICES_PER_CHANNEL as usize] = [ 0x01f0, 0x0170 ]; @@ -26,6 +60,11 @@ const ATAPI_CYLINDER_HIGH_V1: u8 = 0xeb; const ATAPI_CYLINDER_LOW_V2: u8 = 0x69; const ATAPI_CYLINDER_HIGH_V2: u8 = 0x96; +/* ╔═════════════════════════════════════════════════════════════════════════╗ + ║ Enums and structs needed to communicate with the ide controller. ║ + ╚═════════════════════════════════════════════════════════════════════════╝ +*/ + #[derive(Debug, Clone, Copy, PartialEq)] #[repr(u8)] enum DriveType { @@ -83,7 +122,7 @@ enum Command { bitflags! { #[repr(C)] #[derive(Debug, Clone, Copy)] - pub struct Status: u8 { + struct Status: u8 { const None = 0x00; const Error = 0x01; const DataRequest = 0x08; @@ -101,7 +140,7 @@ enum DmaCommand { bitflags! { #[repr(C)] #[derive(Debug, Clone, Copy)] - pub struct DmaStatus: u8 { + struct DmaStatus: u8 { const BusMasterActive = 0x01; const DmaError = 0x02; const Interrupt = 0x04; @@ -111,46 +150,10 @@ bitflags! { } } -#[derive(Copy, Clone)] -#[repr(C, packed)] -struct AtapiInfo { - typ: u8, // Atapi type - packet_length: u8, // Packet length is 12 or 16 - max_sectors_lba: u32, // Size in Sectors - block_size: u32, // Size of data blocks - capacity: u32, // Storage Capacity in Bytes -} - -#[derive(Copy, Clone)] -#[repr(C, packed)] -pub struct DriveInfo { - channel: u8, // 0 (Primary Channel) or 1 (Secondary Channel) - drive: u8, // 0 (Master Drive) or 1 (Slave Drive) - typ: DriveType, // 0 (ATA) or 1 (ATAPI) - cylinders: u16, // Number of logical cylinders of the drive - heads: u16, // Number of logical heads of the drive - sectors_per_track: u16, // Number of sectors per track of the drive - signature: u16, // Drive Signature - capabilities: u16, // Features - multiword_dma: u8, // Supported versions of multiword dma - ultra_dma: u8, // Supported versions of ultra dma - command_sets: [u16; COMMAND_SET_WORD_COUNT], // Supported command sets - max_sectors_lba48: u32, // Size in Sectors LBA48 - max_sectors_lba28: u32, // Size in Sectors LBA28 / CHS - model: [u8; 40], // Model as string - serial: [u8; 10], // Serial number as string - firmware: [u8; 4], // Firmware revision as string - major_version: u16, // Major ATA Version supported - minor_version: u16, // Minor ATA Version supported - addressing: AddressType, // CHS (0), LBA28 (1), LBA48 (2) - sector_size: u16, // Sector size - atapi: AtapiInfo // In case of ATAPI, more information about the drive -} - bitflags! { #[repr(C)] #[derive(Debug, Clone, Copy)] - pub struct PrdFlags: u16 { + struct PrdFlags: u16 { const END_OF_TRANSMISSION = 1 << 15; } } @@ -163,11 +166,26 @@ struct PrdEntry { flags: PrdFlags } +/* ╔═════════════════════════════════════════════════════════════════════════╗ + ║ Register structs. ║ + ║ The registers are divided into three blocks: Control, Command and DMA. ║ + ╚═════════════════════════════════════════════════════════════════════════╝ +*/ + struct ControlRegisters { alternate_status: PortReadOnly, device_control: PortWriteOnly } +impl ControlRegisters { + fn new(base_address: u16) -> Self { + let alternate_status = PortReadOnly::new(base_address + 0x02); + let device_control = PortWriteOnly::new(base_address + 0x02); + + Self { alternate_status, device_control } + } +} + struct CommandRegisters { data: Port, error: PortReadOnly, @@ -183,96 +201,216 @@ struct CommandRegisters { command: PortWriteOnly, } +impl CommandRegisters { + fn new(base_address: u16) -> Self { + let data = Port::new(base_address + 0x00); + let error = PortReadOnly::new(base_address + 0x01); + let sector_count = Port::new(base_address + 0x02); + let sector_number = Port::new(base_address + 0x03); + let lba_low = Port::new(base_address + 0x03); + let cylinder_low = Port::new(base_address + 0x04); + let lba_mid = Port::new(base_address + 0x04); + let cylinder_high = Port::new(base_address + 0x05); + let lba_high = Port::new(base_address + 0x05); + let drive_head = Port::new(base_address + 0x06); + let status = PortReadOnly::new(base_address + 0x07); + let command = PortWriteOnly::new(base_address + 0x07); + + Self { data, error, sector_count, sector_number, lba_low, cylinder_low, lba_mid, cylinder_high, lba_high, drive_head, status, command } + } +} + struct DmaRegisters { command: Port, status: Port, address: Port, } -pub struct IdeDrive { - controller: Arc, - info: DriveInfo -} +impl DmaRegisters { + fn new(base_address: u16) -> Self { + let command = Port::new(base_address + 0x00); + let status = Port::new(base_address + 0x02); + let address = Port::new(base_address + 0x04); -struct Channel { - index: u8, // Channel number - interrupt: InterruptVector, // Interrupt number - supports_dma: bool, // DMA support - received_interrupt: Arc, // Received interrupt flag (shared with interrupt handler) - last_device_control: u8, // Saves current state of deviceControlRegister - interrupts_disabled: bool, // nIEN (No Interrupt) - drive_types: [DriveType; 2], // Initially found drive types - command: CommandRegisters, // Command registers (IO ports) - control: ControlRegisters, // Control registers (IO ports) - dma: DmaRegisters // DMA registers (IO ports) + Self { command, status, address } + } } -pub struct IdeController { - channels: [Mutex; CHANNELS_PER_CONTROLLER as usize] +/* ╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ The actual driver implementation. ║ + ║ The driver is divided into two three structs: IdeController, IdeChannel and IdeDrive. ║ + ║ Each controller manages two channels and each channel can have up to two drives connected to it. ║ + ║ IdeDrive implements the BlockDevice trait, so that the OS can access ide devices. ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝ +*/ + +/// Each IDE controller has two channels, each of which can have up to two drives connected to it. +/// This struct only manages the controller itself. Drive access is implemented in the `IdeChannel` struct. +struct IdeController { + channels: [Mutex; CHANNELS_PER_CONTROLLER as usize] } -pub struct IdeInterruptHandler { - received_interrupt: Arc -} +impl IdeController { + fn new(pci_device: &RwLock) -> Self { + let mut channels: [Mutex; CHANNELS_PER_CONTROLLER as usize] = [Mutex::new(IdeChannel::default()), Mutex::new(IdeChannel::default())]; -impl IdeDrive { - pub fn new(controller: Arc, info: DriveInfo) -> Self { - Self { controller, info } - } + let pci_config_space = pci_bus().config_space(); + let mut pci_device = pci_device.write(); - pub fn read(&self, sector: u64, count: usize, buffer: &mut [u8]) -> usize { - let channel = &mut self.controller.channels[self.info.channel as usize].lock(); - channel.perform_ata_io(&self.info, TransferMode::Read, sector, count, buffer) + let id = pci_device.header().id(&pci_config_space); + let mut rev_and_class = pci_device.header().revision_and_class(&pci_config_space); + info!("Initializing IDE controller [0x{:04x}:0x{:04x}]", id.0, id.1); + + let mut supports_dma = false; + pci_device.update_command(&pci_config_space, |command| { + if rev_and_class.3 & 0x80 == 0x80 { // Bit 7 in programming defines whether the controller supports DMA + info!("IDE controller supports DMA"); + supports_dma = true; + command.bitor(CommandRegister::IO_ENABLE | CommandRegister::BUS_MASTER_ENABLE) + } else { + info!("IDE controller does not support DMA"); + command.bitor(CommandRegister::IO_ENABLE) + } + }); + + for i in 0..CHANNELS_PER_CONTROLLER { + let dma_base_address: u16 = match supports_dma { + true => pci_device.bar(4, &pci_config_space).expect("Failed to read DMA base address").unwrap_io() as u16, + false => 0 + }; + + let mut interface = rev_and_class.3 >> i * 2; // Each channel has two bits in the programming interface + // First bit defines whether the channel is running in compatibility or native mode + // Second bit defines whether mode change is supported + if interface & 0x01 == 0x00 && interface & 0x02 == 0x02 { + info!("Changing mode of channel [{}] to native mode", i); + unsafe { + // Set first bit of channel interface to 1 + let value = pci_config_space.read(pci_device.header().address(), 0x08); + pci_config_space.write(pci_device.header().address(), 0x08, value | (0x01 << i * 2) << 8); + } + + rev_and_class = pci_device.header().revision_and_class(&pci_config_space); + interface = rev_and_class.3 >> i * 2; + } + + let mut interrupts: [InterruptVector; CHANNELS_PER_CONTROLLER as usize] = [InterruptVector::PrimaryAta, InterruptVector::SecondaryAta]; + let command_and_control_base_address = match interface & 0x01 { + 0x00 => { + // Channel is running in compatibility mode -> Use default base address + info!("Channel [{}] is running in compatibility mode", i); + (DEFAULT_BASE_ADDRESSES[i as usize], DEFAULT_CONTROL_BASE_ADDRESSES[i as usize]) + }, + _ => { + // Channel is running in native mode -> Read base address from PCI registers + info!("Channel [{}] is running in native mode", i); + interrupts[i as usize] = InterruptVector::try_from(pci_device.interrupt(&pci_config_space).0).unwrap(); + + (pci_device.bar(0, &pci_config_space).expect("Failed to read command base address").unwrap_io() as u16, + pci_device.bar(1, &pci_config_space).expect("Failed to read control base address").unwrap_io() as u16) + } + }; + + channels[i as usize] = Mutex::new(IdeChannel::new(i, interrupts[i as usize], supports_dma, command_and_control_base_address.0, command_and_control_base_address.1, dma_base_address)); + } + + Self { channels } } - pub fn write(&self, sector: u64, count: usize, buffer: &mut [u8]) -> usize { - let channel = &mut self.controller.channels[self.info.channel as usize].lock(); - channel.perform_ata_io(&self.info, TransferMode::Write, sector, count, buffer) + fn init_drives(&self) -> Vec { + let mut drives: Vec = Vec::new(); + + for channel in self.channels.iter() { + let mut channel = channel.lock(); + for i in 0..DEVICES_PER_CHANNEL { + if !channel.reset_drive(i) { + continue; + } + + if let Some(info) = channel.identify_drive(i) { + info!("Found {:?} drive on channel [{}]: {} {} (Firmware: [{}])", info.typ, channel.index, info.model(), info.serial(), info.firmware()); + drives.push(info); + } + } + } + + drives } -} -impl ControlRegisters { - pub fn new(base_address: u16) -> Self { - let alternate_status = PortReadOnly::new(base_address + 0x02); - let device_control = PortWriteOnly::new(base_address + 0x02); + fn plugin(controller: Arc) { + let primary_channel = controller.channels[0].lock(); + let secondary_channel = controller.channels[1].lock(); - Self { alternate_status, device_control } + interrupt_dispatcher().assign(primary_channel.interrupt, Box::new(IdeInterruptHandler::new(Arc::clone(&primary_channel.received_interrupt)))); + apic().allow(primary_channel.interrupt); + + interrupt_dispatcher().assign(secondary_channel.interrupt, Box::new(IdeInterruptHandler::new(Arc::clone(&secondary_channel.received_interrupt)))); + apic().allow(secondary_channel.interrupt); + } + + fn copy_byte_swapped_string(source: &[u16], target: &mut [u8]) { + for i in (0..target.len()).step_by(2) { + let bytes = source[i / 2]; + target[i] = ((bytes & 0xff00) >> 8) as u8; + target[i + 1] = (bytes & 0x00ff) as u8; + } } } -impl CommandRegisters { - pub fn new(base_address: u16) -> Self { - let data = Port::new(base_address + 0x00); - let error = PortReadOnly::new(base_address + 0x01); - let sector_count = Port::new(base_address + 0x02); - let sector_number = Port::new(base_address + 0x03); - let lba_low = Port::new(base_address + 0x03); - let cylinder_low = Port::new(base_address + 0x04); - let lba_mid = Port::new(base_address + 0x04); - let cylinder_high = Port::new(base_address + 0x05); - let lba_high = Port::new(base_address + 0x05); - let drive_head = Port::new(base_address + 0x06); - let status = PortReadOnly::new(base_address + 0x07); - let command = PortWriteOnly::new(base_address + 0x07); +/// A drive connected to an IDE controller +/// Each drive has a reference to its controller and knows its channel via the `info.channel` filed. +/// It implements the `BlockDevice` trait by calling `perform_ata_io()` on the channel. +pub struct IdeDrive { + controller: Arc, + info: DriveInfo +} - Self { data, error, sector_count, sector_number, lba_low, cylinder_low, lba_mid, cylinder_high, lba_high, drive_head, status, command } +impl IdeDrive { + fn new(controller: Arc, info: DriveInfo) -> Self { + Self { controller, info } } } -impl DmaRegisters { - pub fn new(base_address: u16) -> Self { - let command = Port::new(base_address + 0x00); - let status = Port::new(base_address + 0x02); - let address = Port::new(base_address + 0x04); +impl BlockDevice for IdeDrive { + fn read(&self, sector: u64, count: usize, buffer: &mut [u8]) -> usize { + let channel = &mut self.controller.channels[self.info.channel as usize].lock(); + channel.perform_ata_io(&self.info, TransferMode::Read, sector, count, buffer) + } - Self { command, status, address } + fn write(&self, sector: u64, count: usize, buffer: &[u8]) -> usize { + // Channel::perform_ata_io() expects a mutable buffer, so we need to cast it to a mutable slice. + // This is safe, as the buffer is not modified by the function. + let buffer = unsafe { slice::from_raw_parts_mut(buffer.as_ptr().cast_mut(), buffer.len()) }; + + let channel = &mut self.controller.channels[self.info.channel as usize].lock(); + channel.perform_ata_io(&self.info, TransferMode::Write, sector, count, buffer) } } -impl Default for AtapiInfo { - fn default() -> Self { - Self { typ: 0, packet_length: 0, max_sectors_lba: 0, block_size: 0, capacity: 0 } - } +/// Information about a drive connected to an IDE controller +/// This struct is created in the `IdeChannel::identify_drive()` method. +#[derive(Copy, Clone)] +struct DriveInfo { + channel: u8, // 0 (Primary Channel) or 1 (Secondary Channel) + drive: u8, // 0 (Master Drive) or 1 (Slave Drive) + typ: DriveType, // 0 (ATA) or 1 (ATAPI) + cylinders: u16, // Number of logical cylinders of the drive + heads: u16, // Number of logical heads of the drive + sectors_per_track: u16, // Number of sectors per track of the drive + signature: u16, // Drive Signature + capabilities: u16, // Features + multiword_dma: u8, // Supported versions of multiword dma + ultra_dma: u8, // Supported versions of ultra dma + command_sets: [u16; COMMAND_SET_WORD_COUNT], // Supported command sets + max_sectors_lba48: u32, // Size in Sectors LBA48 + max_sectors_lba28: u32, // Size in Sectors LBA28 / CHS + model: [u8; 40], // Model as string + serial: [u8; 10], // Serial number as string + firmware: [u8; 4], // Firmware revision as string + major_version: u16, // Major ATA Version supported + minor_version: u16, // Minor ATA Version supported + addressing: AddressType, // CHS (0), LBA28 (1), LBA48 (2) + sector_size: u16, // Sector size } impl Default for DriveInfo { @@ -297,8 +435,7 @@ impl Default for DriveInfo { major_version: 0, minor_version: 0, addressing: AddressType::Chs, - sector_size: 0, - atapi: AtapiInfo::default() + sector_size: 0 } } } @@ -321,26 +458,29 @@ impl DriveInfo { } } -impl IdeInterruptHandler { - pub fn new(received_interrupt: Arc) -> Self { - Self { received_interrupt } - } -} - -impl InterruptHandler for IdeInterruptHandler { - fn trigger(&self) { - self.received_interrupt.store(true, Ordering::Relaxed); - } +/// The channel contains the main part of the IDE driver. +/// It manages the communication with the drives and the DMA controller. +struct IdeChannel { + index: u8, // Channel number + interrupt: InterruptVector, // Interrupt number + supports_dma: bool, // DMA support + received_interrupt: Arc, // Received interrupt flag (shared with interrupt handler) + last_device_control: u8, // Saves current state of deviceControlRegister + interrupts_disabled: bool, // nIEN (No Interrupt) + drive_types: [DriveType; 2], // Initially found drive types + command: CommandRegisters, // Command registers (IO ports) + control: ControlRegisters, // Control registers (IO ports) + dma: DmaRegisters // DMA registers (IO ports) } -impl Default for Channel { +impl Default for IdeChannel { fn default() -> Self { Self::new(0 , InterruptVector::PrimaryAta, false, 0, 0, 0) } } -impl Channel { - pub fn new(index: u8, interrupt: InterruptVector, supports_dma: bool, command_base_address: u16, control_base_address: u16, dma_base_address: u16) -> Self { +impl IdeChannel { + fn new(index: u8, interrupt: InterruptVector, supports_dma: bool, command_base_address: u16, control_base_address: u16, dma_base_address: u16) -> Self { let command = CommandRegisters::new(command_base_address); let control = ControlRegisters::new(control_base_address); let dma = DmaRegisters::new(dma_base_address); @@ -353,7 +493,9 @@ impl Channel { last_device_control: u8::MAX, interrupts_disabled: false, drive_types: [DriveType::Other, DriveType::Other], - command, control, dma + command, + control, + dma } } @@ -844,109 +986,21 @@ impl Channel { } } -impl IdeController { - pub fn new(pci_device: &RwLock) -> Self { - let mut channels: [Mutex; CHANNELS_PER_CONTROLLER as usize] = [Mutex::new(Channel::default()), Mutex::new(Channel::default())]; - - let pci_config_space = pci_bus().config_space(); - let mut pci_device = pci_device.write(); - - let id = pci_device.header().id(&pci_config_space); - let mut rev_and_class = pci_device.header().revision_and_class(&pci_config_space); - info!("Initializing IDE controller [0x{:04x}:0x{:04x}]", id.0, id.1); - - let mut supports_dma = false; - pci_device.update_command(&pci_config_space, |command| { - if rev_and_class.3 & 0x80 == 0x80 { // Bit 7 in programming defines whether the controller supports DMA - info!("IDE controller supports DMA"); - supports_dma = true; - command.bitor(CommandRegister::IO_ENABLE | CommandRegister::BUS_MASTER_ENABLE) - } else { - info!("IDE controller does not support DMA"); - command.bitor(CommandRegister::IO_ENABLE) - } - }); - - for i in 0..CHANNELS_PER_CONTROLLER { - let dma_base_address: u16 = match supports_dma { - true => pci_device.bar(4, &pci_config_space).expect("Failed to read DMA base address").unwrap_io() as u16, - false => 0 - }; - - let mut interface = rev_and_class.3 >> i * 2; // Each channel has two bits in the programming interface - // First bit defines whether the channel is running in compatibility or native mode - // Second bit defines whether mode change is supported - if interface & 0x01 == 0x00 && interface & 0x02 == 0x02 { - info!("Changing mode of channel [{}] to native mode", i); - unsafe { - // Set first bit of channel interface to 1 - let value = pci_config_space.read(pci_device.header().address(), 0x08); - pci_config_space.write(pci_device.header().address(), 0x08, value | (0x01 << i * 2) << 8); - } - - rev_and_class = pci_device.header().revision_and_class(&pci_config_space); - interface = rev_and_class.3 >> i * 2; - } - - let mut interrupts: [InterruptVector; CHANNELS_PER_CONTROLLER as usize] = [InterruptVector::PrimaryAta, InterruptVector::SecondaryAta]; - let command_and_control_base_address = match interface & 0x01 { - 0x00 => { - // Channel is running in compatibility mode -> Use default base address - info!("Channel [{}] is running in compatibility mode", i); - (DEFAULT_BASE_ADDRESSES[i as usize], DEFAULT_CONTROL_BASE_ADDRESSES[i as usize]) - }, - _ => { - // Channel is running in native mode -> Read base address from PCI registers - info!("Channel [{}] is running in native mode", i); - interrupts[i as usize] = InterruptVector::try_from(pci_device.interrupt(&pci_config_space).0).unwrap(); - - (pci_device.bar(0, &pci_config_space).expect("Failed to read command base address").unwrap_io() as u16, - pci_device.bar(1, &pci_config_space).expect("Failed to read control base address").unwrap_io() as u16) - } - }; - - channels[i as usize] = Mutex::new(Channel::new(i, interrupts[i as usize], supports_dma, command_and_control_base_address.0, command_and_control_base_address.1, dma_base_address)); - } - - Self { channels } - } - - pub fn init_drives(&self) -> Vec { - let mut drives: Vec = Vec::new(); - - for channel in self.channels.iter() { - let mut channel = channel.lock(); - for i in 0..DEVICES_PER_CHANNEL { - if !channel.reset_drive(i) { - continue; - } - - if let Some(info) = channel.identify_drive(i) { - info!("Found {:?} drive on channel [{}]: {} {} (Firmware: [{}])", info.typ, channel.index, info.model(), info.serial(), info.firmware()); - drives.push(info); - } - } - } - - drives - } - - pub fn plugin(controller: Arc) { - let primary_channel = controller.channels[0].lock(); - let secondary_channel = controller.channels[1].lock(); - - interrupt_dispatcher().assign(primary_channel.interrupt, Box::new(IdeInterruptHandler::new(Arc::clone(&primary_channel.received_interrupt)))); - apic().allow(primary_channel.interrupt); +/// Each channel has its own interrupt handler with a reference to the channel's `received_interrupt` flag. +/// Once an interrupt occurs, the handler sets the flag to `true`. This usually means, that a DMA transfer has finished. +/// It must be set to `false` manually by the channel before starting a new DMA transfer. +pub struct IdeInterruptHandler { + received_interrupt: Arc +} - interrupt_dispatcher().assign(secondary_channel.interrupt, Box::new(IdeInterruptHandler::new(Arc::clone(&secondary_channel.received_interrupt)))); - apic().allow(secondary_channel.interrupt); +impl IdeInterruptHandler { + fn new(received_interrupt: Arc) -> Self { + Self { received_interrupt } } +} - fn copy_byte_swapped_string(source: &[u16], target: &mut [u8]) { - for i in (0..target.len()).step_by(2) { - let bytes = source[i / 2]; - target[i] = ((bytes & 0xff00) >> 8) as u8; - target[i + 1] = (bytes & 0x00ff) as u8; - } +impl InterruptHandler for IdeInterruptHandler { + fn trigger(&self) { + self.received_interrupt.store(true, Ordering::Relaxed); } } \ No newline at end of file diff --git a/os/kernel/src/storage/mod.rs b/os/kernel/src/storage/mod.rs index 85d5e17..538a0e5 100644 --- a/os/kernel/src/storage/mod.rs +++ b/os/kernel/src/storage/mod.rs @@ -1,41 +1,52 @@ +use alloc::format; +use alloc::string::{String, ToString}; use alloc::sync::Arc; -use alloc::vec::Vec; use log::info; -use spin::{Once, RwLock}; -use crate::device::ide::{IdeController, IdeDrive}; -use crate::pci_bus; +use smallmap::Map; +use spin::{Mutex, Once, RwLock}; +use crate::device::ide; -static IDE_CONTROLLER: Once> = Once::new(); -static IDE_DRIVES: RwLock>> = RwLock::new(Vec::new()); +static BLOCK_DEVICES: Once>>> = Once::new(); +static DEVICE_TYPES: Once>> = Once::new(); +/// Trait for accessing devices that can read and write data in fixed-size blocks (sectors) +/// This is the interface that the filesystems will use to access the storage devices +/// Sector addressing uses LBA (Logical Block Addressing) starting from 0 +pub trait BlockDevice { + fn read(&self, sector: u64, count: usize, buffer: &mut [u8]) -> usize; + fn write(&self, sector: u64, count: usize, buffer: &[u8]) -> usize; +} + +/// Initialize all storage drivers pub fn init() { - let devices = pci_bus().search_by_class(0x01, 0x01); - if devices.len() > 0 { - IDE_CONTROLLER.call_once(|| { - info!("Found IDE controller"); - let ide_controller = Arc::new(IdeController::new(devices[0])); - let found_drives = ide_controller.init_drives(); - let mut drives = IDE_DRIVES.write(); - - for drive in found_drives.iter() { - drives.push(Arc::new(IdeDrive::new(Arc::clone(&ide_controller), *drive))); - } - - IdeController::plugin(Arc::clone(&ide_controller)); - ide_controller - }); - } + ide::init(); +} + +/// Register a block device with the given type +/// The type is used to generate a unique name for the device (e.g. type "ata" will generate names "ata0", "ata1", etc.) +pub fn add_block_device(typ: &str, drive: Arc) { + let typ = typ.to_string(); + let mut types = DEVICE_TYPES.call_once(|| Mutex::new(Map::new())).lock(); + let index = *types.get(&typ).unwrap_or(&0); + let name = format!("{}{}", typ, index); + types.insert(typ, index + 1); + + let mut drives = BLOCK_DEVICES.call_once(|| RwLock::new(Map::new())).write(); + drives.insert(name.clone(), drive); + + info!("Registered block device [{}]", name); } -pub fn ide_drive(num: usize) -> Option> { - let drives = IDE_DRIVES.read(); - if num < drives.len() { - Some(Arc::clone(&drives[num])) - } else { - None +/// Get a block device by its name +pub fn block_device(name: &str) -> Option> { + match BLOCK_DEVICES.call_once(|| RwLock::new(Map::new())).read().get(name) { + None => None, + Some(device) => Some(Arc::clone(device)) } } +/// Convert a Logical Block Address (LBA) to Cylinder-Head-Sector (CHS) addressing +/// This is a helper function, that may be used by drivers for legacy devices pub fn lba_to_chs(lba: u64, heads: u8, sectors_per_cylinder: u8) -> (u16, u8, u8) { let cylinder = (lba / (heads as u64 * sectors_per_cylinder as u64)) as u16; let head = (lba % (heads as u64 * sectors_per_cylinder as u64)) as u8;