mirror of https://github.com/vinc/moros.git
Support disks in ATA PIO Mode (#3)
* Introduce ATA PIO * Refactor drives detection * Refactor drive identification * Add read from disk * Add write to disk * Add Bus#setup to dry read/write code * Add hex viewer
This commit is contained in:
parent
3b0121ab9d
commit
b77e02a472
|
@ -4,7 +4,7 @@ set -e
|
|||
|
||||
dir=$(dirname "$0")
|
||||
image="target/x86_64-moros/release/bootimage-moros.bin"
|
||||
qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime"
|
||||
qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -rtc base=localtime -hdc disk.img"
|
||||
|
||||
# Build image if needed
|
||||
cd "$dir/.." && cargo bootimage --release
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
use bit_field::BitField;
|
||||
use crate::{print, kernel};
|
||||
use lazy_static::lazy_static;
|
||||
use heapless::{String, Vec};
|
||||
use heapless::consts::*;
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
|
||||
|
||||
#[repr(u16)]
|
||||
enum Command {
|
||||
Read = 0x20,
|
||||
Write = 0x30,
|
||||
Identify = 0xEC,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)]
|
||||
enum Status {
|
||||
ERR = 0,
|
||||
IDX = 1,
|
||||
CORR = 2,
|
||||
DRQ = 3,
|
||||
SRV = 4,
|
||||
DF = 5,
|
||||
RDY = 6,
|
||||
BSY = 7
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bus {
|
||||
id: u8,
|
||||
irq: u8,
|
||||
|
||||
data_register: Port<u16>,
|
||||
error_register: PortReadOnly<u8>,
|
||||
features_register: PortWriteOnly<u8>,
|
||||
sector_count_register: Port<u8>,
|
||||
lba0_register: Port<u8>,
|
||||
lba1_register: Port<u8>,
|
||||
lba2_register: Port<u8>,
|
||||
drive_register: Port<u8>,
|
||||
status_register: PortReadOnly<u8>,
|
||||
command_register: PortWriteOnly<u8>,
|
||||
|
||||
alternate_status_register: PortReadOnly<u8>,
|
||||
control_register: PortWriteOnly<u8>,
|
||||
drive_blockess_register: PortReadOnly<u8>,
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
pub fn new(id: u8, io_base: u16, ctrl_base: u16, irq: u8) -> Self {
|
||||
Self {
|
||||
id, irq,
|
||||
|
||||
data_register: Port::new(io_base + 0),
|
||||
error_register: PortReadOnly::new(io_base + 1),
|
||||
features_register: PortWriteOnly::new(io_base + 1),
|
||||
sector_count_register: Port::new(io_base + 2),
|
||||
lba0_register: Port::new(io_base + 3),
|
||||
lba1_register: Port::new(io_base + 4),
|
||||
lba2_register: Port::new(io_base + 5),
|
||||
drive_register: Port::new(io_base + 6),
|
||||
status_register: PortReadOnly::new(io_base + 7),
|
||||
command_register: PortWriteOnly::new(io_base + 7),
|
||||
|
||||
alternate_status_register: PortReadOnly::new(ctrl_base + 0),
|
||||
control_register: PortWriteOnly::new(ctrl_base + 0),
|
||||
drive_blockess_register: PortReadOnly::new(ctrl_base + 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
unsafe {
|
||||
self.control_register.write(4);
|
||||
self.control_register.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait(&mut self) {
|
||||
unsafe {
|
||||
self.status_register.read();
|
||||
self.status_register.read();
|
||||
self.status_register.read();
|
||||
self.status_register.read();
|
||||
}
|
||||
}
|
||||
|
||||
fn write_command(&mut self, cmd: Command) {
|
||||
unsafe { self.command_register.write(cmd as u8); }
|
||||
//unsafe { self.command_register.write(cmd as u32); }
|
||||
}
|
||||
|
||||
fn status(&mut self) -> u8 {
|
||||
unsafe { self.status_register.read() }
|
||||
}
|
||||
|
||||
fn lba1(&mut self) -> u8 {
|
||||
unsafe { self.lba1_register.read() }
|
||||
}
|
||||
|
||||
fn lba2(&mut self) -> u8 {
|
||||
unsafe { self.lba2_register.read() }
|
||||
}
|
||||
|
||||
fn read_data(&mut self) -> u16 {
|
||||
unsafe { self.data_register.read() }
|
||||
}
|
||||
|
||||
fn write_data(&mut self, data: u16) {
|
||||
unsafe { self.data_register.write(data) }
|
||||
}
|
||||
|
||||
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 select_drive(&mut self, drive: u8) {
|
||||
// Drive #0 (primary) = 0xA0
|
||||
// Drive #1 (secondary) = 0xB0
|
||||
let drive_id = 0xA0 | (drive << 4);
|
||||
unsafe {
|
||||
self.drive_register.write(drive_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn debug(&mut self) {
|
||||
self.wait();
|
||||
unsafe { print!("drive register: 0b{:08b}\n", self.drive_register.read()); }
|
||||
unsafe { print!("status: 0b{:08b}\n", self.status_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);
|
||||
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);
|
||||
|
||||
if self.status() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
while self.is_busy() {}
|
||||
|
||||
if self.lba1() != 0 || self.lba2() != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
for i in 0.. {
|
||||
if i == 256 {
|
||||
self.reset();
|
||||
return None;
|
||||
}
|
||||
if self.is_error() {
|
||||
return None;
|
||||
}
|
||||
if self.is_ready() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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]) {
|
||||
self.setup(drive, block);
|
||||
|
||||
self.write_command(Command::Read);
|
||||
|
||||
while self.is_busy() {}
|
||||
|
||||
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: &mut [u8]) {
|
||||
self.setup(drive, block);
|
||||
|
||||
self.write_command(Command::Write);
|
||||
|
||||
while self.is_busy() {}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref ATA_BUSES: Mutex<Vec<Bus, U2>> = Mutex::new(Vec::new());
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let mut buses = ATA_BUSES.lock();
|
||||
buses.push(Bus::new(0, 0x1F0, 0x3F6, 14)).unwrap();
|
||||
buses.push(Bus::new(1, 0x170, 0x376, 15)).unwrap();
|
||||
|
||||
let bus = 1;
|
||||
let drive = 0;
|
||||
if let Some(buf) = buses[bus].identify_drive(drive) {
|
||||
let mut serial = String::<U32>::new();
|
||||
for i in 10..20 {
|
||||
for &b in &buf[i].to_be_bytes() {
|
||||
serial.push(b as char).unwrap();
|
||||
}
|
||||
}
|
||||
let mut model = String::<U64>::new();
|
||||
for i in 27..47 {
|
||||
for &b in &buf[i].to_be_bytes() {
|
||||
model.push(b as char).unwrap();
|
||||
}
|
||||
}
|
||||
//let sectors = (buf[60] as u32) << 16 | (buf[61] as u32);
|
||||
let uptime = kernel::clock::clock_monotonic();
|
||||
print!("[{:.6}] ATA {}:{} {} {}\n", uptime, bus, drive, model.trim(), serial.trim());
|
||||
}
|
||||
|
||||
/*
|
||||
let block = 1;
|
||||
let mut buf = [0u8; 512];
|
||||
buses[1].read(drive, block, &mut buf);
|
||||
for i in 0..256 {
|
||||
if i % 8 == 0 {
|
||||
print!("\n{:08X} ", i * 2);
|
||||
}
|
||||
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
|
||||
}
|
||||
print!("\n");
|
||||
|
||||
buf[0x42] = 'H' as u8;
|
||||
buf[0x43] = 'e' as u8;
|
||||
buf[0x44] = 'l' as u8;
|
||||
buf[0x45] = 'l' as u8;
|
||||
buf[0x46] = 'o' as u8;
|
||||
for i in 0..256 {
|
||||
if i % 8 == 0 {
|
||||
print!("\n{:08X} ", i * 2);
|
||||
}
|
||||
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
|
||||
}
|
||||
print!("\n");
|
||||
buses[1].write(drive, block, &mut buf);
|
||||
|
||||
let mut buf = [0u8; 512];
|
||||
buses[1].read(drive, block, &mut buf);
|
||||
for i in 0..256 {
|
||||
if i % 8 == 0 {
|
||||
print!("\n{:08X} ", i * 2);
|
||||
}
|
||||
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
|
||||
}
|
||||
print!("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn read(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) {
|
||||
let mut buses = ATA_BUSES.lock();
|
||||
buses[bus as usize].read(drive, block, &mut buf);
|
||||
}
|
||||
|
||||
pub fn write(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) {
|
||||
let mut buses = ATA_BUSES.lock();
|
||||
buses[bus as usize].write(drive, block, &mut buf);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod acpi;
|
||||
pub mod ata;
|
||||
pub mod clock;
|
||||
pub mod cmos;
|
||||
pub mod console;
|
||||
|
|
|
@ -12,4 +12,5 @@ pub fn init() {
|
|||
x86_64::instructions::interrupts::enable();
|
||||
kernel::cpu::init();
|
||||
kernel::pci::init();
|
||||
kernel::ata::init();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
use crate::{print, kernel, user};
|
||||
use heapless::Vec;
|
||||
use heapless::consts::*;
|
||||
|
||||
pub fn main(args: &[&str]) -> user::shell::ExitCode {
|
||||
if args.len() != 2 {
|
||||
return user::shell::ExitCode::CommandError;
|
||||
}
|
||||
let path: Vec<_, U16> = args[1].split("/").collect();
|
||||
if path.len() != 8 || path[1] != "dev" || path[2] != "ata" || path[4] != "dsk" || path[6] != "blk" {
|
||||
return user::shell::ExitCode::CommandError;
|
||||
}
|
||||
let bus = path[3].parse().unwrap();
|
||||
let dsk = path[5].parse().unwrap();
|
||||
let blk = path[7].parse().unwrap();
|
||||
let mut buf = [0u8; 512];
|
||||
kernel::ata::read(bus, dsk, blk, &mut buf);
|
||||
print_hex(&buf);
|
||||
user::shell::ExitCode::CommandSuccessful
|
||||
}
|
||||
|
||||
pub fn print_hex(buf: &[u8]) {
|
||||
let n = buf.len() / 2;
|
||||
for i in 0..n {
|
||||
if i % 8 == 0 {
|
||||
print!("\n{:08X}: ", i * 2);
|
||||
}
|
||||
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
|
||||
if i % 8 == 7 {
|
||||
for j in 0..16 {
|
||||
let c = buf[(i * 2 + 1) - 15 + j] as char;
|
||||
if c.is_ascii_graphic() || c.is_ascii_whitespace() {
|
||||
print!("{}", c);
|
||||
} else {
|
||||
print!(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
}
|
|
@ -3,6 +3,7 @@ pub mod clear;
|
|||
pub mod date;
|
||||
pub mod editor;
|
||||
pub mod halt;
|
||||
pub mod hex;
|
||||
pub mod login;
|
||||
pub mod print;
|
||||
pub mod r#move;
|
||||
|
|
|
@ -219,6 +219,7 @@ impl Shell {
|
|||
"login" => user::login::main(&args),
|
||||
"base64" => user::base64::main(&args),
|
||||
"halt" => user::halt::main(&args),
|
||||
"hex" => user::hex::main(&args),
|
||||
_ => ExitCode::CommandUnknown,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue