Skip to main content

strat9_kernel/hardware/storage/
ata_legacy.rs

1// Legacy ATA/IDE Driver (PIOS and DMA)
2// Reference: ATA/ATAPI-7 Specification
3
4use alloc::{format, string::String, sync::Arc, vec::Vec};
5use core::sync::atomic::{AtomicBool, Ordering};
6use spin::Mutex;
7
8use super::virtio_block::{BlockDevice, BlockError, SECTOR_SIZE};
9
10const ATA_PRIMARY_IO: u16 = 0x1F0;
11const ATA_SECONDARY_IO: u16 = 0x170;
12
13#[allow(dead_code)]
14const ATA_REG_DATA: usize = 0;
15#[allow(dead_code)]
16const ATA_REG_ERROR: usize = 1;
17const ATA_REG_SECCOUNT: usize = 2;
18const ATA_REG_LBA_LOW: usize = 3;
19const ATA_REG_LBA_MID: usize = 4;
20const ATA_REG_LBA_HIGH: usize = 5;
21const ATA_REG_DEVICE: usize = 6;
22const ATA_REG_STATUS: usize = 7;
23const ATA_REG_COMMAND: usize = 7;
24
25const ATA_SR_BSY: u8 = 0x80;
26#[allow(dead_code)]
27const ATA_SR_DRDY: u8 = 0x40;
28const ATA_SR_DRQ: u8 = 0x08;
29const ATA_SR_ERR: u8 = 0x01;
30
31const ATA_CMD_IDENTIFY: u8 = 0xEC;
32
33const ATA_DEVICE_MASTER: u8 = 0xA0;
34const ATA_DEVICE_SLAVE: u8 = 0xB0;
35const ATA_DEVICE_LBA: u8 = 0x40;
36
37#[derive(Clone, Copy)]
38pub struct AtaChannel {
39    io_base: u16,
40    #[allow(dead_code)]
41    control_base: u16,
42    bus: u8,
43}
44
45impl AtaChannel {
46    /// Creates a new instance.
47    fn new(io_base: u16, bus: u8) -> Self {
48        Self {
49            io_base,
50            control_base: io_base + 0x206,
51            bus,
52        }
53    }
54
55    /// Performs the read8 operation.
56    fn read8(&self, offset: usize) -> u8 {
57        unsafe {
58            crate::x86_crate_shim::instructions::port::Port::new(self.io_base + offset as u16)
59                .read()
60        }
61    }
62
63    /// Performs the write8 operation.
64    fn write8(&self, offset: usize, value: u8) {
65        unsafe {
66            crate::x86_crate_shim::instructions::port::Port::new(self.io_base + offset as u16)
67                .write(value)
68        }
69    }
70
71    /// Performs the read16 operation.
72    fn read16(&self) -> u16 {
73        unsafe { crate::x86_crate_shim::instructions::port::Port::new(self.io_base).read() }
74    }
75
76    /// Performs the write16 operation.
77    fn write16(&self, value: u16) {
78        unsafe { crate::x86_crate_shim::instructions::port::Port::new(self.io_base).write(value) }
79    }
80
81    /// Performs the wait ready operation.
82    fn wait_ready(&self) -> Result<(), &'static str> {
83        for _ in 0..100000 {
84            let status = self.read8(ATA_REG_STATUS);
85            if (status & ATA_SR_BSY) == 0 {
86                return Ok(());
87            }
88            core::hint::spin_loop();
89        }
90        Err("ATA timeout")
91    }
92
93    /// Performs the wait drq operation.
94    fn wait_drq(&self) -> Result<(), &'static str> {
95        for _ in 0..100000 {
96            let status = self.read8(ATA_REG_STATUS);
97            if (status & ATA_SR_DRQ) != 0 {
98                return Ok(());
99            }
100            if (status & ATA_SR_ERR) != 0 {
101                return Err("ATA error");
102            }
103            core::hint::spin_loop();
104        }
105        Err("ATA timeout")
106    }
107
108    /// Performs the select device operation.
109    fn select_device(&self, _device: u8, lba: u64) {
110        let device_reg = ATA_DEVICE_MASTER | ATA_DEVICE_LBA | ((lba >> 24) & 0x0F) as u8;
111        self.write8(ATA_REG_DEVICE, device_reg);
112        self.read8(ATA_REG_STATUS);
113        for _ in 0..4 {
114            core::hint::spin_loop();
115        }
116    }
117
118    /// Performs the identify operation.
119    fn identify(&self, device: u8) -> Option<AtaDriveInfo> {
120        self.select_device(device, 0);
121        self.write8(ATA_REG_SECCOUNT, 0);
122        self.write8(ATA_REG_LBA_LOW, 0);
123        self.write8(ATA_REG_LBA_MID, 0);
124        self.write8(ATA_REG_LBA_HIGH, 0);
125        self.write8(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
126
127        let status = self.read8(ATA_REG_STATUS);
128        if status == 0 {
129            return None;
130        }
131
132        if let Err(_) = self.wait_ready() {
133            return None;
134        }
135
136        if let Err(_) = self.wait_drq() {
137            return None;
138        }
139
140        let mut buffer = [0u16; 256];
141        for i in 0..256 {
142            buffer[i] = self.read16();
143        }
144
145        let serial = Self::decode_identify_string(&buffer, 10, 20);
146        let model = Self::decode_identify_string(&buffer, 27, 54);
147        let capacity = (buffer[60] as u64) | ((buffer[61] as u64) << 16);
148
149        Some(AtaDriveInfo {
150            model,
151            serial,
152            capacity,
153        })
154    }
155
156    /// Performs the decode identify string operation.
157    fn decode_identify_string(buffer: &[u16], start: usize, end: usize) -> String {
158        use alloc::string::String;
159        let mut s = String::new();
160        for i in start..end {
161            if i < buffer.len() {
162                let c = buffer[i];
163                let hi = (c >> 8) as u8;
164                let lo = (c & 0xFF) as u8;
165                if hi != 0 {
166                    s.push(hi as char);
167                }
168                if lo != 0 {
169                    s.push(lo as char);
170                }
171            }
172        }
173        s
174    }
175
176    /// Reads sector pio.
177    fn read_sector_pio(&self, device: u8, lba: u64, buffer: &mut [u8]) -> Result<(), &'static str> {
178        if buffer.len() < SECTOR_SIZE {
179            return Err("Buffer too small");
180        }
181
182        self.wait_ready()?;
183        self.select_device(device, lba);
184
185        self.write8(ATA_REG_SECCOUNT, 1);
186        self.write8(ATA_REG_LBA_LOW, (lba & 0xFF) as u8);
187        self.write8(ATA_REG_LBA_MID, ((lba >> 8) & 0xFF) as u8);
188        self.write8(ATA_REG_LBA_HIGH, ((lba >> 16) & 0xFF) as u8);
189
190        self.write8(ATA_REG_COMMAND, 0x24);
191        self.wait_drq()?;
192
193        let buf_ptr = buffer.as_mut_ptr() as *mut u16;
194        for i in 0..256 {
195            unsafe {
196                core::ptr::write_volatile(buf_ptr.add(i), self.read16());
197            }
198        }
199
200        self.wait_ready()?;
201        Ok(())
202    }
203
204    /// Writes sector pio.
205    fn write_sector_pio(&self, device: u8, lba: u64, buffer: &[u8]) -> Result<(), &'static str> {
206        if buffer.len() < SECTOR_SIZE {
207            return Err("Buffer too small");
208        }
209
210        self.wait_ready()?;
211        self.select_device(device, lba);
212
213        self.write8(ATA_REG_SECCOUNT, 1);
214        self.write8(ATA_REG_LBA_LOW, (lba & 0xFF) as u8);
215        self.write8(ATA_REG_LBA_MID, ((lba >> 8) & 0xFF) as u8);
216        self.write8(ATA_REG_LBA_HIGH, ((lba >> 16) & 0xFF) as u8);
217
218        self.write8(ATA_REG_COMMAND, 0x34);
219        self.wait_drq()?;
220
221        let buf_ptr = buffer.as_ptr() as *const u16;
222        for i in 0..256 {
223            unsafe {
224                self.write16(core::ptr::read_volatile(buf_ptr.add(i)));
225            }
226        }
227
228        self.wait_ready()?;
229        Ok(())
230    }
231}
232
233#[derive(Clone)]
234pub struct AtaDriveInfo {
235    pub model: String,
236    pub serial: String,
237    pub capacity: u64,
238}
239
240pub struct AtaDrive {
241    channel: AtaChannel,
242    device: u8,
243    info: AtaDriveInfo,
244    #[allow(dead_code)]
245    name: String,
246}
247
248unsafe impl Send for AtaDrive {}
249unsafe impl Sync for AtaDrive {}
250
251impl AtaDrive {
252    /// Creates a new instance.
253    pub fn new(channel: AtaChannel, device: u8) -> Option<Self> {
254        let info = channel.identify(device)?;
255        let name = format!(
256            "ata{}_{}",
257            channel.bus,
258            if device == ATA_DEVICE_MASTER {
259                "master"
260            } else {
261                "slave"
262            }
263        );
264        Some(Self {
265            channel,
266            device,
267            info,
268            name,
269        })
270    }
271
272    /// Performs the info operation.
273    pub fn info(&self) -> &AtaDriveInfo {
274        &self.info
275    }
276}
277
278impl BlockDevice for AtaDrive {
279    /// Reads sector.
280    fn read_sector(&self, sector: u64, buf: &mut [u8]) -> Result<(), BlockError> {
281        self.channel
282            .read_sector_pio(self.device, sector, buf)
283            .map_err(|_| BlockError::IoError)
284    }
285
286    /// Writes sector.
287    fn write_sector(&self, sector: u64, buf: &[u8]) -> Result<(), BlockError> {
288        self.channel
289            .write_sector_pio(self.device, sector, buf)
290            .map_err(|_| BlockError::IoError)
291    }
292
293    /// Performs the sector count operation.
294    fn sector_count(&self) -> u64 {
295        self.info.capacity
296    }
297}
298
299static ATA_DRIVES: Mutex<Vec<Arc<AtaDrive>>> = Mutex::new(Vec::new());
300static ATA_INITIALIZED: AtomicBool = AtomicBool::new(false);
301
302/// Performs the init operation.
303pub fn init() {
304    log::info!("[ATA] Scanning for legacy ATA/IDE devices...");
305
306    let channels = [
307        AtaChannel::new(ATA_PRIMARY_IO, 0),
308        AtaChannel::new(ATA_SECONDARY_IO, 1),
309    ];
310
311    for channel in &channels {
312        for device in [ATA_DEVICE_MASTER, ATA_DEVICE_SLAVE] {
313            if let Some(drive) = AtaDrive::new(channel.clone(), device) {
314                log::info!(
315                    "ATA: Found drive on bus{} device{}: {} ({} sectors)",
316                    channel.bus,
317                    if device == ATA_DEVICE_MASTER {
318                        "master"
319                    } else {
320                        "slave"
321                    },
322                    drive.info().model,
323                    drive.info().capacity
324                );
325                ATA_DRIVES.lock().push(Arc::new(drive));
326            }
327        }
328    }
329
330    ATA_INITIALIZED.store(true, Ordering::SeqCst);
331    log::info!("[ATA] Found {} drive(s)", ATA_DRIVES.lock().len());
332}
333
334/// Returns drive.
335pub fn get_drive(index: usize) -> Option<Arc<AtaDrive>> {
336    ATA_DRIVES.lock().get(index).cloned()
337}
338
339/// Returns first drive.
340pub fn get_first_drive() -> Option<Arc<AtaDrive>> {
341    ATA_DRIVES.lock().first().cloned()
342}
343
344/// Returns whether available.
345pub fn is_available() -> bool {
346    ATA_INITIALIZED.load(Ordering::Relaxed)
347}