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