mirror of https://github.com/vinc/moros.git
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:
parent
1dc1fa8dfc
commit
20a4bd37d0
329
src/sys/ata.rs
329
src/sys/ata.rs
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue