Improve ATA driver (#286)

* Improve identify data parsing

* Read status from alternate status register

* Update identify command

* Fix overflow in disk size printing

* Rewrite ATA PIO driver according to ATA-4 spec

* Add 'disk erase' command

* Fix issue with shell redirection in diskless mode

* Check for existence of passwords file during login

* Add IdentifyResponse enum

* Refactor lba match

* Disable debug output on command aborted

* Refactor code

* Pin specific compiler version

* Add spec in comment
This commit is contained in:
Vincent Ollivier 2021-12-19 10:03:09 +01:00 committed by GitHub
parent 1dc1fa8dfc
commit 20a4bd37d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 277 additions and 202 deletions

View File

@ -2,32 +2,44 @@ use crate::sys;
use alloc::string::String;
use alloc::vec::Vec;
use bit_field::BitField;
use core::convert::TryInto;
use core::fmt;
use core::hint::spin_loop;
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
// See "Information Technology - AT Attachment with Packet Interface Extension (ATA/ATAPI-4)" (1998)
pub const BLOCK_SIZE: usize = 512;
#[repr(u16)]
#[derive(Debug, Clone, Copy)]
enum Command {
Read = 0x20,
Write = 0x30,
Identify = 0xEC,
}
enum IdentifyResponse {
Ata([u16; 256]),
Atapi,
Sata,
None,
}
#[allow(dead_code)]
#[repr(usize)]
#[derive(Debug, Clone, Copy)]
enum Status {
ERR = 0,
IDX = 1,
CORR = 2,
DRQ = 3,
SRV = 4,
DF = 5,
RDY = 6,
BSY = 7,
ERR = 0, // Error
IDX = 1, // (obsolete)
CORR = 2, // (obsolete)
DRQ = 3, // Data Request
DSC = 4, // (command dependant)
DF = 5, // (command dependant)
DRDY = 6, // Device Ready
BSY = 7, // Busy
}
#[allow(dead_code)]
@ -74,27 +86,23 @@ impl Bus {
}
}
fn reset(&mut self) {
unsafe {
self.control_register.write(4); // Set SRST bit
sys::time::nanowait(5); // Wait at least 5 us
self.control_register.write(0); // Then clear it
sys::time::nanowait(2000); // Wait at least 2 ms
fn check_floating_bus(&mut self) -> Result<(), ()> {
match self.status() {
0xFF | 0x7F => Err(()),
_ => Ok(()),
}
}
fn wait(&mut self) {
sys::time::nanowait(400); // Wait at least 400 us
fn wait(&mut self, ns: u64) {
sys::time::nanowait(ns);
}
fn write_command(&mut self, cmd: Command) {
unsafe {
self.command_register.write(cmd as u8);
}
fn clear_interrupt(&mut self) -> u8 {
unsafe { self.status_register.read() }
}
fn status(&mut self) -> u8 {
unsafe { self.status_register.read() }
unsafe { self.alternate_status_register.read() }
}
fn lba1(&mut self) -> u8 {
@ -113,130 +121,151 @@ impl Bus {
unsafe { self.data_register.write(data) }
}
fn busy_loop(&mut self) {
self.wait();
let start = sys::clock::uptime();
while self.is_busy() {
if sys::clock::uptime() - start > 1.0 { // Hanged
return self.reset();
}
spin_loop();
}
}
fn is_busy(&mut self) -> bool {
self.status().get_bit(Status::BSY as usize)
}
fn is_error(&mut self) -> bool {
self.status().get_bit(Status::ERR as usize)
}
fn is_ready(&mut self) -> bool {
self.status().get_bit(Status::RDY as usize)
fn poll(&mut self, bit: Status, val: bool) -> Result<(), ()> {
let start = sys::clock::uptime();
while self.status().get_bit(bit as usize) != val {
if sys::clock::uptime() - start > 1.0 {
debug!("ATA hanged while polling {:?} bit in status register", bit);
self.debug();
return Err(());
}
spin_loop();
}
Ok(())
}
fn select_drive(&mut self, drive: u8) {
// Drive #0 (primary) = 0xA0
// Drive #1 (secondary) = 0xB0
let drive_id = 0xA0 | (drive << 4);
fn select_drive(&mut self, drive: u8) -> Result<(), ()> {
self.poll(Status::BSY, false)?;
self.poll(Status::DRQ, false)?;
unsafe {
self.drive_register.write(drive_id);
// Bit 4 => DEV
// Bit 5 => 1
// Bit 7 => 1
self.drive_register.write(0xA0 | (drive << 4))
}
sys::time::nanowait(400); // Wait at least 400 ns
self.poll(Status::BSY, false)?;
self.poll(Status::DRQ, false)?;
Ok(())
}
fn write_command_params(&mut self, drive: u8, block: u32) -> Result<(), ()> {
let lba = true;
let mut bytes = block.to_le_bytes();
bytes[3].set_bit(4, drive > 0);
bytes[3].set_bit(5, true);
bytes[3].set_bit(6, lba);
bytes[3].set_bit(7, true);
unsafe {
self.sector_count_register.write(1);
self.lba0_register.write(bytes[0]);
self.lba1_register.write(bytes[1]);
self.lba2_register.write(bytes[2]);
self.drive_register.write(bytes[3]);
}
Ok(())
}
fn write_command(&mut self, cmd: Command) -> Result<(), ()> {
unsafe { self.command_register.write(cmd as u8) }
self.wait(400); // Wait at least 400 ns
self.status(); // Ignore results of first read
self.clear_interrupt();
if self.status() == 0 { // Drive does not exist
return Err(());
}
if self.is_error() {
//debug!("ATA {:?} command errored", cmd);
//self.debug();
return Err(());
}
self.poll(Status::BSY, false)?;
self.poll(Status::DRQ, true)?;
Ok(())
}
fn setup_pio(&mut self, drive: u8, block: u32) -> Result<(), ()> {
self.select_drive(drive)?;
self.write_command_params(drive, block)?;
Ok(())
}
fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) -> Result<(), ()> {
debug_assert!(buf.len() == BLOCK_SIZE);
self.setup_pio(drive, block)?;
self.write_command(Command::Read)?;
for chunk in buf.chunks_mut(2) {
let data = self.read_data().to_le_bytes();
chunk.clone_from_slice(&data);
}
if self.is_error() {
debug!("ATA read: data error");
self.debug();
Err(())
} else {
Ok(())
}
}
fn write(&mut self, drive: u8, block: u32, buf: &[u8]) -> Result<(), ()> {
debug_assert!(buf.len() == BLOCK_SIZE);
self.setup_pio(drive, block)?;
self.write_command(Command::Write)?;
for chunk in buf.chunks(2) {
let data = u16::from_le_bytes(chunk.try_into().unwrap());
self.write_data(data);
}
if self.is_error() {
debug!("ATA write: data error");
self.debug();
Err(())
} else {
Ok(())
}
}
fn identify_drive(&mut self, drive: u8) -> Result<IdentifyResponse, ()> {
if self.check_floating_bus().is_err() {
return Ok(IdentifyResponse::None);
}
self.select_drive(drive)?;
self.write_command_params(drive, 0)?;
if self.write_command(Command::Identify).is_err() {
if self.status() == 0 {
return Ok(IdentifyResponse::None);
} else {
return Err(());
}
}
match (self.lba1(), self.lba2()) {
(0x00, 0x00) => Ok(IdentifyResponse::Ata([(); 256].map(|_| { self.read_data() }))),
(0x14, 0xEB) => Ok(IdentifyResponse::Atapi),
(0x3C, 0x3C) => Ok(IdentifyResponse::Sata),
(_, _) => Err(()),
}
}
#[allow(dead_code)]
fn reset(&mut self) {
unsafe {
self.control_register.write(4); // Set SRST bit
self.wait(5); // Wait at least 5 ns
self.control_register.write(0); // Then clear it
self.wait(2000); // Wait at least 2 ms
}
}
#[allow(dead_code)]
fn debug(&mut self) {
self.wait();
unsafe {
printk!("drive register: 0b{:08b}\n", self.drive_register.read());
printk!("status: 0b{:08b}\n", self.status_register.read());
debug!("ATA status register: 0b{:08b} <BSY|DRDY|#|#|DRQ|#|#|ERR>", self.alternate_status_register.read());
debug!("ATA error register: 0b{:08b} <#|#|#|#|#|ABRT|#|#>", self.error_register.read());
}
}
fn setup(&mut self, drive: u8, block: u32) {
let drive_id = 0xE0 | (drive << 4);
unsafe {
self.drive_register.write(drive_id | ((block.get_bits(24..28) as u8) & 0x0F));
self.sector_count_register.write(1);
self.lba0_register.write(block.get_bits(0..8) as u8);
self.lba1_register.write(block.get_bits(8..16) as u8);
self.lba2_register.write(block.get_bits(16..24) as u8);
}
}
pub fn identify_drive(&mut self, drive: u8) -> Option<[u16; 256]> {
self.reset();
self.wait();
self.select_drive(drive);
self.wait();
unsafe {
self.sector_count_register.write(0);
self.lba0_register.write(0);
self.lba1_register.write(0);
self.lba2_register.write(0);
}
self.write_command(Command::Identify);
self.wait();
if self.status() == 0 {
return None;
}
self.busy_loop();
if self.lba1() != 0 || self.lba2() != 0 {
return None;
}
for i in 0.. {
if i == 25 { // Waited 10ms (400ns * 25)
self.reset();
return None;
}
if self.is_error() {
return None;
}
if self.is_ready() {
break;
}
self.wait();
}
let mut res = [0; 256];
for i in 0..256 {
res[i] = self.read_data();
}
Some(res)
}
pub fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) {
assert!(buf.len() == BLOCK_SIZE);
self.setup(drive, block);
self.write_command(Command::Read);
self.busy_loop();
for i in 0..256 {
let data = self.read_data();
buf[i * 2] = data.get_bits(0..8) as u8;
buf[i * 2 + 1] = data.get_bits(8..16) as u8;
}
}
pub fn write(&mut self, drive: u8, block: u32, buf: &[u8]) {
assert!(buf.len() == BLOCK_SIZE);
self.setup(drive, block);
self.write_command(Command::Write);
self.busy_loop();
for i in 0..256 {
let mut data = 0 as u16;
data.set_bits(0..8, buf[i * 2] as u16);
data.set_bits(8..16, buf[i * 2 + 1] as u16);
self.write_data(data);
}
self.busy_loop();
}
}
lazy_static! {
@ -265,25 +294,13 @@ pub struct Drive {
}
impl Drive {
pub fn identify(bus: u8, dsk: u8) -> Option<Self> {
pub fn open(bus: u8, dsk: u8) -> Option<Self> {
let mut buses = BUSES.lock();
if let Some(buf) = buses[bus as usize].identify_drive(dsk) {
let mut serial = String::new();
for i in 10..20 {
for &b in &buf[i].to_be_bytes() {
serial.push(b as char);
}
}
serial = serial.trim().into();
let mut model = String::new();
for i in 27..47 {
for &b in &buf[i].to_be_bytes() {
model.push(b as char);
}
}
model = model.trim().into();
// Total number of 28-bit LBA addressable blocks
let blocks = (buf[61] as u32) << 16 | (buf[60] as u32);
if let Ok(IdentifyResponse::Ata(res)) = buses[bus as usize].identify_drive(dsk) {
let buf = res.map(u16::to_be_bytes).concat();
let serial = String::from_utf8_lossy(&buf[20..40]).trim().into();
let model = String::from_utf8_lossy(&buf[54..94]).trim().into();
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).rotate_left(16);
Some(Self { bus, dsk, model, serial, blocks })
} else {
None
@ -298,8 +315,10 @@ impl Drive {
self.blocks
}
fn humanized_size(&self) -> (u32, String) {
let bytes = self.block_size() * self.block_count();
fn humanized_size(&self) -> (usize, String) {
let size = self.block_size() as usize;
let count = self.block_count() as usize;
let bytes = size * count;
if bytes >> 20 < 1000 {
(bytes >> 20, String::from("MB"))
} else {
@ -319,7 +338,7 @@ pub fn list() -> Vec<Drive> {
let mut res = Vec::new();
for bus in 0..2 {
for dsk in 0..2 {
if let Some(drive) = Drive::identify(bus, dsk) {
if let Some(drive) = Drive::open(bus, dsk) {
res.push(drive)
}
}
@ -327,12 +346,12 @@ pub fn list() -> Vec<Drive> {
res
}
pub fn read(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) {
pub fn read(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) -> Result<(), ()> {
let mut buses = BUSES.lock();
buses[bus as usize].read(drive, block, &mut buf);
buses[bus as usize].read(drive, block, &mut buf)
}
pub fn write(bus: u8, drive: u8, block: u32, buf: &[u8]) {
pub fn write(bus: u8, drive: u8, block: u32, buf: &[u8]) -> Result<(), ()> {
let mut buses = BUSES.lock();
buses[bus as usize].write(drive, block, buf);
buses[bus as usize].write(drive, block, buf)
}

View File

@ -43,14 +43,18 @@ impl Block {
pub fn read(addr: u32) -> Self {
let mut buf = [0; super::BLOCK_SIZE];
if let Some(ref block_device) = *super::block_device::BLOCK_DEVICE.lock() {
block_device.read(addr, &mut buf);
if block_device.read(addr, &mut buf).is_err() {
debug!("MFS: could not read block {:#x}", addr);
}
}
Self { addr, buf }
}
pub fn write(&self) {
if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() {
block_device.write(self.addr, &self.buf);
if block_device.write(self.addr, &self.buf).is_err() {
debug!("MFS: could not write block {:#x}", self.addr);
}
}
}

View File

@ -19,21 +19,21 @@ pub enum BlockDevice {
}
pub trait BlockDeviceIO {
fn read(&self, addr: u32, buf: &mut [u8]);
fn write(&mut self, addr: u32, buf: &[u8]);
fn read(&self, addr: u32, buf: &mut [u8]) -> Result<(), ()>;
fn write(&mut self, addr: u32, buf: &[u8]) -> Result<(), ()>;
fn block_size(&self) -> usize;
fn block_count(&self) -> usize;
}
impl BlockDeviceIO for BlockDevice {
fn read(&self, addr: u32, buf: &mut [u8]) {
fn read(&self, addr: u32, buf: &mut [u8]) -> Result<(), ()> {
match self {
BlockDevice::Mem(dev) => dev.read(addr, buf),
BlockDevice::Ata(dev) => dev.read(addr, buf),
}
}
fn write(&mut self, addr: u32, buf: &[u8]) {
fn write(&mut self, addr: u32, buf: &[u8]) -> Result<(), ()> {
match self {
BlockDevice::Mem(dev) => dev.write(addr, buf),
BlockDevice::Ata(dev) => dev.write(addr, buf),
@ -73,12 +73,16 @@ impl MemBlockDevice {
}
impl BlockDeviceIO for MemBlockDevice {
fn read(&self, block_index: u32, buf: &mut [u8]) {
fn read(&self, block_index: u32, buf: &mut [u8]) -> Result<(), ()> {
// TODO: check for overflow
buf[..].clone_from_slice(&self.dev[block_index as usize][..]);
Ok(())
}
fn write(&mut self, block_index: u32, buf: &[u8]) {
fn write(&mut self, block_index: u32, buf: &[u8]) -> Result<(), ()> {
// TODO: check for overflow
self.dev[block_index as usize][..].clone_from_slice(&buf[..]);
Ok(())
}
fn block_size(&self) -> usize {
@ -113,11 +117,9 @@ pub struct AtaBlockDevice {
impl AtaBlockDevice {
pub fn new(bus: u8, dsk: u8) -> Option<Self> {
if let Some(dev) = sys::ata::Drive::identify(bus, dsk) {
Some(Self { dev })
} else {
None
}
sys::ata::Drive::open(bus, dsk).map(|dev| {
Self { dev }
})
}
/*
@ -128,12 +130,12 @@ impl AtaBlockDevice {
}
impl BlockDeviceIO for AtaBlockDevice {
fn read(&self, block_addr: u32, mut buf: &mut [u8]) {
sys::ata::read(self.dev.bus, self.dev.dsk, block_addr, &mut buf);
fn read(&self, block_addr: u32, mut buf: &mut [u8]) -> Result<(), ()> {
sys::ata::read(self.dev.bus, self.dev.dsk, block_addr, &mut buf)
}
fn write(&mut self, block_addr: u32, buf: &[u8]) {
sys::ata::write(self.dev.bus, self.dev.dsk, block_addr, buf);
fn write(&mut self, block_addr: u32, buf: &[u8]) -> Result<(), ()> {
sys::ata::write(self.dev.bus, self.dev.dsk, block_addr, buf)
}
fn block_size(&self) -> usize {

View File

@ -19,7 +19,9 @@ pub struct SuperBlock {
impl SuperBlock {
pub fn check_ata(bus: u8, dsk: u8) -> bool {
let mut buf = [0u8; super::BLOCK_SIZE];
sys::ata::read(bus, dsk, SUPERBLOCK_ADDR, &mut buf);
if sys::ata::read(bus, dsk, SUPERBLOCK_ADDR, &mut buf).is_err() {
return false;
}
&buf[0..8] == SIGNATURE
}

View File

@ -1,29 +1,24 @@
use crate::{sys, usr};
use crate::api::console::Style;
use crate::api::io;
use crate::sys::ata::Drive;
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
pub fn main(args: &[&str]) -> usr::shell::ExitCode {
if args.len() == 1 {
return usage();
}
match args[1] {
"format" => {
if args.len() == 2 {
return help();
}
format(args[2])
},
"usage" => {
usage()
},
"list" => {
list()
},
_ => {
help()
}
"format" if args.len() == 3 => format(args[2]),
"erase" if args.len() == 3 => erase(args[2]),
"usage" => usage(),
"list" => list(),
_ => help(),
}
}
@ -32,27 +27,71 @@ fn help() -> usr::shell::ExitCode {
println!();
println!("Commands:");
println!(" format <path>");
println!(" erase <path>");
println!(" list");
println!(" usage");
usr::shell::ExitCode::CommandSuccessful
}
fn format(pathname: &str) -> usr::shell::ExitCode {
fn parse_disk_path(pathname: &str) -> Result<(u8, u8), String> {
let path: Vec<_> = pathname.split('/').collect();
if !pathname.starts_with("/dev/ata/") || path.len() != 5 {
eprintln!("Could not find disk at '{}'", pathname);
return usr::shell::ExitCode::CommandError;
return Err(format!("Could not find disk at '{}'", pathname));
}
let bus = path[3].parse().or(Err("Could not parse <bus>".to_string()))?;
let dsk = path[4].parse().or(Err("Could not parse <dsk>".to_string()))?;
Ok((bus, dsk))
}
let bus = path[3].parse().expect("Could not parse <bus>");
let dsk = path[4].parse().expect("Could not parse <dsk>");
sys::fs::mount_ata(bus, dsk);
sys::fs::format_ata();
println!("Disk successfully formatted");
println!("MFS is now mounted to '/'");
fn format(pathname: &str) -> usr::shell::ExitCode {
match parse_disk_path(pathname) {
Ok((bus, dsk)) => {
sys::fs::mount_ata(bus, dsk);
sys::fs::format_ata();
println!("Disk successfully formatted");
println!("MFS is now mounted to '/'");
usr::shell::ExitCode::CommandSuccessful
}
Err(msg) => {
eprintln!("{}", msg);
usr::shell::ExitCode::CommandError
}
}
}
usr::shell::ExitCode::CommandSuccessful
fn erase(pathname: &str) -> usr::shell::ExitCode {
match parse_disk_path(pathname) {
Ok((bus, dsk)) => {
if let Some(drive) = Drive::open(bus, dsk) {
print!("Proceed? [y/N] ");
if io::stdin().read_line().trim() == "y" {
println!();
let n = drive.block_count();
let buf = vec![0; drive.block_size() as usize];
print!("\x1b[?25l"); // Disable cursor
for i in 0..n {
if sys::console::end_of_text() {
println!();
print!("\x1b[?25h"); // Enable cursor
return usr::shell::ExitCode::CommandError;
}
print!("\x1b[2K\x1b[1G");
print!("Erasing block {}/{}", i, n);
// TODO: Implement drive.write(block, buf)
sys::ata::write(bus, dsk, i, &buf).ok();
}
println!();
print!("\x1b[?25h"); // Enable cursor
}
}
usr::shell::ExitCode::CommandSuccessful
}
Err(msg) => {
eprintln!("{}", msg);
usr::shell::ExitCode::CommandError
}
}
}
fn list() -> usr::shell::ExitCode {

View File

@ -129,6 +129,7 @@ pub fn exec(cmd: &str) -> ExitCode {
// Redirections like `print hello => /tmp/hello`
// Pipes like `print hello -> write /tmp/hello` or `p hello > w /tmp/hello`
let mut is_redirected = false;
let mut n = args.len();
let mut i = 0;
loop {
@ -161,6 +162,7 @@ pub fn exec(cmd: &str) -> ExitCode {
}
if is_fat_arrow { // Redirections
is_redirected = true;
if i == n - 1 {
println!("Could not parse path for redirection");
return ExitCode::CommandError;
@ -248,8 +250,10 @@ pub fn exec(cmd: &str) -> ExitCode {
};
// TODO: Remove this when redirections are done in spawned process
for i in 0..3 {
api::fs::reopen("/dev/console", i).ok();
if is_redirected {
for i in 0..3 {
api::fs::reopen("/dev/console", i).ok();
}
}
return res;

View File

@ -41,6 +41,11 @@ fn usage() -> usr::shell::ExitCode {
// TODO: Add max number of attempts
pub fn login(username: &str) -> usr::shell::ExitCode {
if !fs::exists(PASSWORDS) {
eprintln!("Could not read '{}'", PASSWORDS);
return usr::shell::ExitCode::CommandError;
}
if username.is_empty() {
println!();
syscall::sleep(1.0);