···11+//! ATA/IDE disk driver (PIO mode)
22+//!
33+//! This driver supports reading from ATA hard drives using PIO (Programmed I/O) mode.
44+//! It implements the BlockDevice trait for integration with the VFS layer.
55+//!
66+//! **Supported:**
77+//! - Primary bus (0x1F0-0x1F7)
88+//! - Master drive
99+//! - 28-bit LBA addressing
1010+//! - Read operations
1111+//!
1212+//! **Not yet implemented:**
1313+//! - Secondary bus (0x170)
1414+//! - Slave drives
1515+//! - 48-bit LBA
1616+//! - DMA mode
1717+//! - Write operations
1818+1919+use crate::vfs::block_device::{BlockDevice, BlockDeviceError};
2020+use alloc::vec::Vec;
2121+use core::arch::asm;
2222+2323+/// ATA command codes
2424+const ATA_CMD_READ_SECTORS: u8 = 0x20;
2525+const ATA_CMD_IDENTIFY: u8 = 0xEC;
2626+2727+/// ATA status register bits
2828+const ATA_STATUS_BSY: u8 = 0x80; // Busy
2929+const ATA_STATUS_DRQ: u8 = 0x08; // Data Request Ready
3030+const ATA_STATUS_ERR: u8 = 0x01; // Error
3131+3232+/// Primary ATA bus base I/O port
3333+const ATA_PRIMARY_BASE: u16 = 0x1F0;
3434+3535+/// I/O port offsets from base
3636+const ATA_REG_DATA: u16 = 0; // 0x1F0
3737+const ATA_REG_ERROR: u16 = 1; // 0x1F1
3838+const ATA_REG_SECTOR_COUNT: u16 = 2; // 0x1F2
3939+const ATA_REG_LBA_LOW: u16 = 3; // 0x1F3
4040+const ATA_REG_LBA_MID: u16 = 4; // 0x1F4
4141+const ATA_REG_LBA_HIGH: u16 = 5; // 0x1F5
4242+const ATA_REG_DRIVE: u16 = 6; // 0x1F6
4343+const ATA_REG_STATUS: u16 = 7; // 0x1F7
4444+const ATA_REG_COMMAND: u16 = 7; // 0x1F7 (write)
4545+4646+/// ATA disk drive
4747+pub struct AtaDrive {
4848+ bus: u16,
4949+ drive: u8, // 0 = master, 1 = slave
5050+ sectors: u64,
5151+ sector_size: u32,
5252+}
5353+5454+impl AtaDrive {
5555+ /// Get the total number of sectors on this drive
5656+ pub fn sector_count(&self) -> u64 {
5757+ self.sectors
5858+ }
5959+6060+ /// Detect and initialize primary master drive
6161+ pub fn detect_primary_master() -> Option<Self> {
6262+ let bus = ATA_PRIMARY_BASE;
6363+ let drive = 0;
6464+6565+ // Aggressive detection: just assume drive exists and try to use it
6666+ // If sector reads work, it's a real drive!
6767+ // This avoids IDENTIFY command which was hanging
6868+ Some(AtaDrive {
6969+ bus,
7070+ drive,
7171+ sectors: 204800, // Assume 100MB (will be corrected if FAT32 mount works)
7272+ sector_size: 512,
7373+ })
7474+ }
7575+7676+ /// Send IDENTIFY command to drive
7777+ fn identify(bus: u16, drive: u8) -> bool {
7878+ // Select drive
7979+ outb(bus + ATA_REG_DRIVE, 0xA0 | (drive << 4));
8080+ Self::wait_400ns(bus);
8181+8282+ // Set sector count and LBA to 0
8383+ outb(bus + ATA_REG_SECTOR_COUNT, 0);
8484+ outb(bus + ATA_REG_LBA_LOW, 0);
8585+ outb(bus + ATA_REG_LBA_MID, 0);
8686+ outb(bus + ATA_REG_LBA_HIGH, 0);
8787+8888+ // Send IDENTIFY command
8989+ outb(bus + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
9090+9191+ // Read status
9292+ let status = inb(bus + ATA_REG_STATUS);
9393+ if status == 0 {
9494+ // Drive does not exist
9595+ return false;
9696+ }
9797+9898+ // Poll until BSY clears
9999+ if !Self::wait_not_busy(bus) {
100100+ return false;
101101+ }
102102+103103+ // Check if drive is ready
104104+ let lba_mid = inb(bus + ATA_REG_LBA_MID);
105105+ let lba_high = inb(bus + ATA_REG_LBA_HIGH);
106106+107107+ if lba_mid != 0 || lba_high != 0 {
108108+ // Not ATA drive (might be ATAPI)
109109+ return false;
110110+ }
111111+112112+ // Wait for DRQ or ERR (with timeout)
113113+ for _ in 0..1000 {
114114+ let status = inb(bus + ATA_REG_STATUS);
115115+ if status & ATA_STATUS_ERR != 0 {
116116+ return false;
117117+ }
118118+ if status & ATA_STATUS_DRQ != 0 {
119119+ break;
120120+ }
121121+ }
122122+123123+ // Drive exists and responded to IDENTIFY
124124+ // Discard the 256 words of IDENTIFY data for now
125125+ for _ in 0..256 {
126126+ let _ = inw(bus + ATA_REG_DATA);
127127+ }
128128+129129+ true
130130+ }
131131+132132+ /// Read sector count from IDENTIFY data
133133+ fn read_sector_count(bus: u16, drive: u8) -> u64 {
134134+ // Re-send IDENTIFY to get data
135135+ outb(bus + ATA_REG_DRIVE, 0xA0 | (drive << 4));
136136+ Self::wait_400ns(bus);
137137+ outb(bus + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
138138+ Self::wait_not_busy(bus);
139139+140140+ // Wait for DRQ (with timeout)
141141+ let mut drq_ready = false;
142142+ for _ in 0..1000 {
143143+ let status = inb(bus + ATA_REG_STATUS);
144144+ if status & ATA_STATUS_DRQ != 0 {
145145+ drq_ready = true;
146146+ break;
147147+ }
148148+ }
149149+150150+ if !drq_ready {
151151+ // Timeout - return default
152152+ return 2048; // 1MB default
153153+ }
154154+155155+ // Read IDENTIFY data
156156+ let mut identify_data = [0u16; 256];
157157+ for i in 0..256 {
158158+ identify_data[i] = inw(bus + ATA_REG_DATA);
159159+ }
160160+161161+ // Words 60-61 contain total 28-bit LBA sectors
162162+ let sectors_low = identify_data[60] as u32;
163163+ let sectors_high = identify_data[61] as u32;
164164+ let sectors = (sectors_high as u64) << 16 | sectors_low as u64;
165165+166166+ if sectors > 0 {
167167+ sectors
168168+ } else {
169169+ // Default to small size if IDENTIFY failed
170170+ 2048 // 1MB
171171+ }
172172+ }
173173+174174+ /// Read a single sector (28-bit LBA)
175175+ fn read_sector_pio(&self, lba: u64) -> Result<Vec<u8>, BlockDeviceError> {
176176+ if lba >= self.sectors {
177177+ return Err(BlockDeviceError::InvalidSector);
178178+ }
179179+180180+ // Select drive (LBA mode)
181181+ let drive_byte = 0xE0 | (self.drive << 4) | ((lba >> 24) & 0x0F) as u8;
182182+ outb(self.bus + ATA_REG_DRIVE, drive_byte);
183183+ Self::wait_400ns(self.bus);
184184+185185+ // Send sector count (1 sector)
186186+ outb(self.bus + ATA_REG_SECTOR_COUNT, 1);
187187+188188+ // Send LBA
189189+ outb(self.bus + ATA_REG_LBA_LOW, (lba & 0xFF) as u8);
190190+ outb(self.bus + ATA_REG_LBA_MID, ((lba >> 8) & 0xFF) as u8);
191191+ outb(self.bus + ATA_REG_LBA_HIGH, ((lba >> 16) & 0xFF) as u8);
192192+193193+ // Send READ SECTORS command
194194+ outb(self.bus + ATA_REG_COMMAND, ATA_CMD_READ_SECTORS);
195195+196196+ // Wait for drive to be ready
197197+ if !Self::wait_not_busy(self.bus) {
198198+ return Err(BlockDeviceError::IoError);
199199+ }
200200+201201+ // Wait for DRQ (with timeout)
202202+ for _ in 0..1000 {
203203+ let status = inb(self.bus + ATA_REG_STATUS);
204204+ if status & ATA_STATUS_ERR != 0 {
205205+ return Err(BlockDeviceError::IoError);
206206+ }
207207+ if status & ATA_STATUS_DRQ != 0 {
208208+ break;
209209+ }
210210+ }
211211+212212+ // Read 512 bytes (256 words)
213213+ let mut buffer = Vec::with_capacity(512);
214214+ for _ in 0..256 {
215215+ let word = inw(self.bus + ATA_REG_DATA);
216216+ buffer.push((word & 0xFF) as u8);
217217+ buffer.push((word >> 8) as u8);
218218+ }
219219+220220+ Ok(buffer)
221221+ }
222222+223223+ /// Wait for drive to not be busy (with timeout)
224224+ fn wait_not_busy(bus: u16) -> bool {
225225+ for _ in 0..1000 { // Much shorter timeout
226226+ let status = inb(bus + ATA_REG_STATUS);
227227+ if status & ATA_STATUS_BSY == 0 {
228228+ return true;
229229+ }
230230+ // No delay - inb() itself provides enough time
231231+ }
232232+ false
233233+ }
234234+235235+ /// Wait 400ns by reading status register 4 times
236236+ fn wait_400ns(bus: u16) {
237237+ for _ in 0..4 {
238238+ let _ = inb(bus + ATA_REG_STATUS);
239239+ }
240240+ }
241241+242242+ /// Tiny delay for polling loops
243243+ fn tiny_delay() {
244244+ for _ in 0..100 {
245245+ core::hint::spin_loop();
246246+ }
247247+ }
248248+}
249249+250250+impl BlockDevice for AtaDrive {
251251+ fn sector_size(&self) -> u32 {
252252+ self.sector_size
253253+ }
254254+255255+ fn sector_count(&self) -> u64 {
256256+ self.sectors
257257+ }
258258+259259+ fn read_sector(&self, sector: u64) -> Result<Vec<u8>, BlockDeviceError> {
260260+ self.read_sector_pio(sector)
261261+ }
262262+263263+ fn read_sectors(&self, start_sector: u64, count: u32) -> Result<Vec<u8>, BlockDeviceError> {
264264+ let mut result = Vec::with_capacity((count as usize) * 512);
265265+266266+ for i in 0..count {
267267+ let sector_data = self.read_sector(start_sector + i as u64)?;
268268+ result.extend_from_slice(§or_data);
269269+ }
270270+271271+ Ok(result)
272272+ }
273273+274274+ fn write_sector(&self, _sector: u64, _data: &[u8]) -> Result<(), BlockDeviceError> {
275275+ // Write not yet implemented
276276+ Err(BlockDeviceError::WriteProtected)
277277+ }
278278+279279+ fn write_sectors(&self, _start_sector: u64, _data: &[u8]) -> Result<(), BlockDeviceError> {
280280+ // Write not yet implemented
281281+ Err(BlockDeviceError::WriteProtected)
282282+ }
283283+284284+ fn sync(&self) -> Result<(), BlockDeviceError> {
285285+ // No cache to flush in PIO mode
286286+ Ok(())
287287+ }
288288+289289+ fn is_read_only(&self) -> bool {
290290+ true // For now, until write support is added
291291+ }
292292+}
293293+294294+/// Read byte from I/O port
295295+fn inb(port: u16) -> u8 {
296296+ let result: u8;
297297+ unsafe {
298298+ asm!(
299299+ "in al, dx",
300300+ out("al") result,
301301+ in("dx") port,
302302+ options(nomem, nostack, preserves_flags)
303303+ );
304304+ }
305305+ result
306306+}
307307+308308+/// Write byte to I/O port
309309+fn outb(port: u16, value: u8) {
310310+ unsafe {
311311+ asm!(
312312+ "out dx, al",
313313+ in("dx") port,
314314+ in("al") value,
315315+ options(nomem, nostack, preserves_flags)
316316+ );
317317+ }
318318+}
319319+320320+/// Read word (16-bit) from I/O port
321321+fn inw(port: u16) -> u16 {
322322+ let result: u16;
323323+ unsafe {
324324+ asm!(
325325+ "in ax, dx",
326326+ out("ax") result,
327327+ in("dx") port,
328328+ options(nomem, nostack, preserves_flags)
329329+ );
330330+ }
331331+ result
332332+}
+7
heartwood/src/drivers/mod.rs
···11+//! Hardware device drivers
22+//!
33+//! This module contains drivers for various hardware devices.
44+55+pub mod ata;
66+77+pub use ata::AtaDrive;
+34-61
heartwood/src/eldarin.rs
···946946947947/// VFS Info - Show filesystem information
948948fn cmd_vfs_info() {
949949- use crate::vfs::mock_fat32::MockFat32Device;
950950- use crate::vfs::fat32::Fat32;
951951- use alloc::boxed::Box;
952952-953953- crate::println!("◈ Filesystem Information");
954954- crate::println!();
955955-956956- let device = Box::new(MockFat32Device::new());
957957- let fat32 = match Fat32::new(device) {
958958- Ok(fs) => fs,
959959- Err(e) => {
960960- crate::println!("Failed to mount: {:?}", e);
961961- return;
962962- }
963963- };
964964-965965- crate::println!(" Filesystem Type: {}", fat32.bpb.fs_type);
966966- crate::println!(" Volume Label: {}", fat32.bpb.volume_label);
967967- crate::println!();
968968- crate::println!(" Geometry:");
969969- crate::println!(" Bytes per sector: {}", fat32.bpb.bytes_per_sector);
970970- crate::println!(" Sectors/cluster: {}", fat32.bpb.sectors_per_cluster);
971971- crate::println!(" Cluster size: {} bytes", fat32.bpb.cluster_size());
972972- crate::println!(" Total sectors: {}", fat32.bpb.total_sectors);
973973- crate::println!();
974974- crate::println!(" FAT Information:");
975975- crate::println!(" Number of FATs: {}", fat32.bpb.num_fats);
976976- crate::println!(" Sectors per FAT: {}", fat32.bpb.sectors_per_fat);
977977- crate::println!(" Root cluster: {}", fat32.bpb.root_cluster);
978978- crate::println!();
979979- crate::println!(" Special Sectors:");
980980- crate::println!(" FSInfo sector: {}", fat32.bpb.fsinfo_sector);
981981- crate::println!(" Backup boot: {}", fat32.bpb.backup_boot_sector);
982982-983983- if let Some(free) = fat32.bpb.free_clusters() {
984984- crate::println!();
985985- crate::println!(" Space (from FSInfo):");
986986- crate::println!(" Free clusters: {}", free);
987987- if let Some(space) = fat32.bpb.free_space() {
988988- crate::println!(" Free space: {} KB ({} MB)",
989989- space / 1024, space / 1024 / 1024);
990990- }
991991- }
949949+ crate::vfs::debug_cmd::show_mount_status();
992950}
993951994952/// VFS LS - List directory contents
995953fn cmd_vfs_ls(args: &str) {
996954 use crate::vfs::{FileSystem, Path};
997997- use crate::vfs::mock_fat32::MockFat32Device;
998998- use crate::vfs::fat32::Fat32;
999999- use alloc::boxed::Box;
955955+ use crate::vfs::global as vfs_global;
10009561001957 let path = if args.is_empty() { "/" } else { args.trim() };
100295810031003- let device = Box::new(MockFat32Device::new());
10041004- let fat32 = match Fat32::new(device) {
10051005- Ok(fs) => fs,
10061006- Err(e) => {
10071007- crate::println!("Failed to mount: {:?}", e);
959959+ // Get global filesystem
960960+ let global_fs = match vfs_global::get() {
961961+ Some(fs) => fs,
962962+ None => {
963963+ crate::println!(" ✗ No filesystem mounted");
964964+ crate::println!(" (Reboot with -hda disk.img to mount a drive)");
965965+ return;
966966+ }
967967+ };
968968+969969+ let fs_lock = global_fs.lock();
970970+ let fs = match &*fs_lock {
971971+ Some(filesystem) => filesystem,
972972+ None => {
973973+ crate::println!(" ✗ No filesystem mounted");
1008974 return;
1009975 }
1010976 };
···1012978 crate::println!("◈ Directory listing: {}", path);
1013979 crate::println!();
101498010151015- match fat32.read_dir(&Path::new(path)) {
981981+ match fs.read_dir(&Path::new(path)) {
1016982 Ok(entries) => {
1017983 let count = entries.len();
1018984 if entries.is_empty() {
···10361002/// VFS CAT - Display file contents
10371003fn cmd_vfs_cat(args: &str) {
10381004 use crate::vfs::{FileSystem, Path};
10391039- use crate::vfs::mock_fat32::MockFat32Device;
10401040- use crate::vfs::fat32::Fat32;
10411041- use alloc::boxed::Box;
10051005+ use crate::vfs::global as vfs_global;
1042100610431007 if args.is_empty() {
10441008 crate::println!("Usage: vfs-cat <filename>");
···1048101210491013 let filename = args.trim();
1050101410511051- let device = Box::new(MockFat32Device::new());
10521052- let fat32 = match Fat32::new(device) {
10531053- Ok(fs) => fs,
10541054- Err(e) => {
10551055- crate::println!("Failed to mount: {:?}", e);
10151015+ // Get global filesystem
10161016+ let global_fs = match vfs_global::get() {
10171017+ Some(fs) => fs,
10181018+ None => {
10191019+ crate::println!(" ✗ No filesystem mounted");
10201020+ return;
10211021+ }
10221022+ };
10231023+10241024+ let fs_lock = global_fs.lock();
10251025+ let fs = match &*fs_lock {
10261026+ Some(filesystem) => filesystem,
10271027+ None => {
10281028+ crate::println!(" ✗ No filesystem mounted");
10561029 return;
10571030 }
10581031 };
···10601033 crate::println!("◈ Reading: {}", filename);
10611034 crate::println!();
1062103510631063- match fat32.read(&Path::new(filename)) {
10361036+ match fs.read(&Path::new(filename)) {
10641037 Ok(data) => {
10651038 if let Ok(text) = core::str::from_utf8(&data) {
10661039 for line in text.lines() {
+1
heartwood/src/lib.rs
···4545pub mod eldarin; // The Eldarin Shell
4646pub mod irq_safe_mutex; // Interrupt-safe mutex primitive
4747pub mod vfs; // Virtual File System layer
4848+pub mod drivers; // Hardware device drivers
48494950// Re-export key types
5051pub use nexus::{Message, MessageType, MessagePriority, NexusError};
+42
heartwood/src/main.rs
···108108 }
109109}
110110111111+/// Mount storage - using RAM disk since ATA driver has issues
112112+fn detect_and_mount_storage() {
113113+ use heartwood::vfs::mock_fat32::MockFat32Device;
114114+ use heartwood::vfs::fat32::Fat32;
115115+ use heartwood::vfs::global as vfs_global;
116116+ use alloc::boxed::Box;
117117+118118+ // Initialize global VFS
119119+ vfs_global::init();
120120+121121+ println!(" ◈ Creating RAM disk (64KB FAT32 filesystem)...");
122122+123123+ // Create in-memory FAT32 filesystem
124124+ let device = Box::new(MockFat32Device::new());
125125+126126+ match Fat32::new(device) {
127127+ Ok(fs) => {
128128+ println!(" ✓ RAM disk created and mounted!");
129129+130130+ // Mount globally
131131+ vfs_global::mount(Box::new(fs));
132132+ println!(" ✓ Filesystem accessible (vfs-ls, vfs-cat)");
133133+ println!();
134134+ println!(" Files available:");
135135+ println!(" /README.TXT - Welcome message");
136136+ println!(" /TEST.TXT - Test file");
137137+ }
138138+ Err(e) => {
139139+ println!(" ✗ Failed to create RAM disk: {}", e);
140140+ }
141141+ }
142142+143143+ println!();
144144+ println!(" Note: ATA driver postponed - RAM disk used instead");
145145+}
146146+111147/// Initialize the Heartwood's core systems
112148fn heartwood_init() {
113149 unsafe { serial_out(b'A'); } // Before init sequence
···162198 heartwood::eldarin::init();
163199 unsafe { serial_out(b'M'); }
164200 println!(" ✓ Shell ready");
201201+202202+ // Detect ATA drives and mount filesystem
203203+ unsafe { serial_out(b'N'); }
204204+ println!("◈ Detecting storage devices...");
205205+ detect_and_mount_storage();
206206+ unsafe { serial_out(b'O'); }
165207166208 unsafe { serial_out(b'K'); }
167209 println!("\n◈ Heartwood initialization complete!");
+36
heartwood/src/vfs/debug_cmd.rs
···11+//! VFS Debug Command - shows mount status
22+33+use crate::vfs::global as vfs_global;
44+55+pub fn show_mount_status() {
66+ crate::println!("◈ VFS Mount Status");
77+ crate::println!();
88+99+ match vfs_global::get() {
1010+ None => {
1111+ crate::println!(" ✗ Global VFS not initialized");
1212+ crate::println!(" (This is a bug - VFS should initialize at boot)");
1313+ }
1414+ Some(global_fs) => {
1515+ crate::println!(" ✓ Global VFS initialized");
1616+1717+ let fs_lock = global_fs.lock();
1818+ match &*fs_lock {
1919+ None => {
2020+ crate::println!(" ✗ No filesystem mounted");
2121+ crate::println!();
2222+ crate::println!(" Possible reasons:");
2323+ crate::println!(" - No ATA drive detected (check boot messages)");
2424+ crate::println!(" - FAT32 mount failed (disk not formatted?)");
2525+ crate::println!(" - QEMU not started with -hda disk.img");
2626+ }
2727+ Some(fs) => {
2828+ crate::println!(" ✓ Filesystem mounted!");
2929+ crate::println!(" Type: {}", fs.name());
3030+ crate::println!();
3131+ crate::println!(" Try: vfs-ls / to list root directory");
3232+ }
3333+ }
3434+ }
3535+ }
3636+}
+44
heartwood/src/vfs/global.rs
···11+//! Global VFS mount point
22+//!
33+//! Simple global filesystem for testing until proper VFS manager is implemented
44+55+use super::FileSystem;
66+use crate::mana_pool::InterruptSafeLock;
77+use core::mem::MaybeUninit;
88+use alloc::boxed::Box;
99+1010+/// Global mounted filesystem
1111+static mut GLOBAL_FS: MaybeUninit<InterruptSafeLock<Option<Box<dyn FileSystem>>>> = MaybeUninit::uninit();
1212+static mut FS_INITIALIZED: bool = false;
1313+1414+/// Initialize the global filesystem
1515+pub fn init() {
1616+ unsafe {
1717+ let fs_option: Option<Box<dyn FileSystem>> = None;
1818+ let lock = InterruptSafeLock::new(fs_option);
1919+ core::ptr::write(core::ptr::addr_of_mut!(GLOBAL_FS).cast(), lock);
2020+ FS_INITIALIZED = true;
2121+ }
2222+}
2323+2424+/// Mount a filesystem as the global root
2525+pub fn mount(fs: Box<dyn FileSystem>) {
2626+ unsafe {
2727+ if !FS_INITIALIZED {
2828+ init();
2929+ }
3030+ let global_fs = &*core::ptr::addr_of!(GLOBAL_FS).cast::<InterruptSafeLock<Option<Box<dyn FileSystem>>>>();
3131+ let mut fs_lock = global_fs.lock();
3232+ *fs_lock = Some(fs);
3333+ }
3434+}
3535+3636+/// Get reference to global filesystem
3737+pub fn get() -> Option<&'static InterruptSafeLock<Option<Box<dyn FileSystem>>>> {
3838+ unsafe {
3939+ if !FS_INITIALIZED {
4040+ return None;
4141+ }
4242+ Some(&*core::ptr::addr_of!(GLOBAL_FS).cast::<InterruptSafeLock<Option<Box<dyn FileSystem>>>>())
4343+ }
4444+}
+2
heartwood/src/vfs/mod.rs
···1313pub mod block_device;
1414pub mod fat32;
1515pub mod mock_fat32;
1616+pub mod global;
1717+pub mod debug_cmd;
16181719#[cfg(test)]
1820mod tests;