commit
40e4d23c19
@ -0,0 +1,13 @@
|
|||||||
|
//! stub frame buffer driver for malta board
|
||||||
|
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref FRAME_BUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Framebuffer {}
|
||||||
|
|
||||||
|
pub struct FramebufferInfo {}
|
||||||
|
|
||||||
|
pub enum ColorDepth {}
|
@ -0,0 +1,19 @@
|
|||||||
|
use once::*;
|
||||||
|
|
||||||
|
pub mod serial;
|
||||||
|
pub mod fb;
|
||||||
|
#[path = "../../../../drivers/console/mod.rs"]
|
||||||
|
pub mod console;
|
||||||
|
|
||||||
|
/// Initialize serial port first
|
||||||
|
pub fn init_serial_early() {
|
||||||
|
assert_has_not_been_called!("board::init must be called only once");
|
||||||
|
serial::init(0xbf000900);
|
||||||
|
println!("Hello QEMU Malta!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize other board drivers
|
||||||
|
pub fn init_driver() {
|
||||||
|
// TODO: add possibly more drivers
|
||||||
|
// timer::init();
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
//! 16550 serial adapter driver for malta board
|
||||||
|
|
||||||
|
use core::fmt::{Write, Result, Arguments};
|
||||||
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
struct SerialPort {
|
||||||
|
base: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialPort {
|
||||||
|
fn new() -> SerialPort {
|
||||||
|
SerialPort {
|
||||||
|
base: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, base: usize) {
|
||||||
|
self.base = base;
|
||||||
|
// Turn off the FIFO
|
||||||
|
write(self.base + COM_FCR, 0 as u8);
|
||||||
|
// Set speed; requires DLAB latch
|
||||||
|
write(self.base + COM_LCR, COM_LCR_DLAB);
|
||||||
|
write(self.base + COM_DLL, (115200 / 9600) as u8);
|
||||||
|
write(self.base + COM_DLM, 0 as u8);
|
||||||
|
|
||||||
|
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||||
|
write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB);
|
||||||
|
|
||||||
|
// No modem controls
|
||||||
|
write(self.base + COM_MCR, 0 as u8);
|
||||||
|
// Enable rcv interrupts
|
||||||
|
write(self.base + COM_IER, COM_IER_RDI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of putchar()
|
||||||
|
fn putchar(&mut self, c: u8) {
|
||||||
|
write(self.base + COM_TX, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// blocking version of getchar()
|
||||||
|
pub fn getchar(&mut self) -> char {
|
||||||
|
loop {
|
||||||
|
if (read::<u8>(self.base + COM_LSR) & COM_LSR_DATA) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let c = read::<u8>(self.base + COM_RX);
|
||||||
|
match c {
|
||||||
|
255 => '\0', // null
|
||||||
|
c => c as char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of getchar()
|
||||||
|
pub fn getchar_option(&mut self) -> Option<char> {
|
||||||
|
match read::<u8>(self.base + COM_LSR) & COM_LSR_DATA {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(read::<u8>(self.base + COM_RX) as u8 as char),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||||
|
self.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for SerialPort {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result {
|
||||||
|
for c in s.bytes() {
|
||||||
|
if c == 127 {
|
||||||
|
self.putchar(8);
|
||||||
|
self.putchar(b' ');
|
||||||
|
self.putchar(8);
|
||||||
|
} else {
|
||||||
|
self.putchar(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<T>(addr: usize, content: T) {
|
||||||
|
let cell = (addr) as *mut T;
|
||||||
|
write_volatile(cell, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<T>(addr: usize) -> T {
|
||||||
|
let cell = (addr) as *const T;
|
||||||
|
read_volatile(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const COM_RX :usize = 0; // In: Receive buffer (DLAB=0)
|
||||||
|
const COM_TX :usize = 0; // Out: Transmit buffer (DLAB=0)
|
||||||
|
const COM_DLL :usize = 0; // Out: Divisor Latch Low (DLAB=1)
|
||||||
|
const COM_DLM :usize = 1; // Out: Divisor Latch High (DLAB=1)
|
||||||
|
const COM_IER :usize = 1; // Out: Interrupt Enable Register
|
||||||
|
const COM_IER_RDI :u8 = 0x01; // Enable receiver data interrupt
|
||||||
|
const COM_IIR :usize = 2; // In: Interrupt ID Register
|
||||||
|
const COM_FCR :usize = 2; // Out: FIFO Control Register
|
||||||
|
const COM_LCR :usize = 3; // Out: Line Control Register
|
||||||
|
const COM_LCR_DLAB :u8 = 0x80; // Divisor latch access bit
|
||||||
|
const COM_LCR_WLEN8 :u8 = 0x03; // Wordlength: 8 bits
|
||||||
|
const COM_MCR :usize = 4; // Out: Modem Control Register
|
||||||
|
const COM_MCR_RTS :u8 = 0x02; // RTS complement
|
||||||
|
const COM_MCR_DTR :u8 = 0x01; // DTR complement
|
||||||
|
const COM_MCR_OUT2 :u8 = 0x08; // Out2 complement
|
||||||
|
const COM_LSR :usize = 5; // In: Line Status Register
|
||||||
|
const COM_LSR_DATA :u8 = 0x01; // Data available
|
||||||
|
const COM_LSR_TXRDY :u8 = 0x20; // Transmit buffer avail
|
||||||
|
const COM_LSR_TSRE :u8 = 0x40; // Transmitter off
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(base: usize) {
|
||||||
|
SERIAL_PORT.lock().init(base);
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
|
||||||
|
/ {
|
||||||
|
model = "thinpad trivialmips";
|
||||||
|
compatible = "tsinghua,thinpad";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
stdio = &uart;
|
||||||
|
bootargs = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases { };
|
||||||
|
|
||||||
|
cpu_intc: interrupt-controller {
|
||||||
|
compatible = "mti,cpu-interrupt-controller";
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
memory: memory@80000000 {
|
||||||
|
device_type = "memory";
|
||||||
|
reg = <0x80000000 0x00800000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
bus: trivial_bus@a0000000 {
|
||||||
|
compatible = "thinpad,bus";
|
||||||
|
reg = <0xa0000000 0x800000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
flash: flash@a1000000 {
|
||||||
|
compatible = "cfi-flash";
|
||||||
|
reg = <0xa1000000 0x00800000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
framebuffer: framebuffer@a2000000 {
|
||||||
|
compatible = "thinpad,framebuffer";
|
||||||
|
reg = <0xa2000000 0x75300
|
||||||
|
0xa2075300 0x4>;
|
||||||
|
};
|
||||||
|
|
||||||
|
uart: serial@a3000000 {
|
||||||
|
compatible = "thinpad,uart";
|
||||||
|
reg = <0xa3000000 0x1
|
||||||
|
0xa3000004 0x1>;
|
||||||
|
clock-frequency = <115200>;
|
||||||
|
interrupt-parent = <&cpu_intc>;
|
||||||
|
interrupts = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
timer: gpio@a4000000 {
|
||||||
|
compatible = "thinpad,timer";
|
||||||
|
reg = <0xa400000 0x8>;
|
||||||
|
};
|
||||||
|
|
||||||
|
eth: ethernet@a5000000 {
|
||||||
|
compatible = "davicom,dm9000";
|
||||||
|
reg = <0xa5000000 0x2
|
||||||
|
0xa5000004 0x2>;
|
||||||
|
interrupt-parent = <&cpu_intc>;
|
||||||
|
interrupts = <2>;
|
||||||
|
davicom,no-eeprom;
|
||||||
|
mac-address = [00 0a 2d 98 01 29];
|
||||||
|
};
|
||||||
|
|
||||||
|
gpio: gpio@a6000000 {
|
||||||
|
compatible = "thinpad,gpio";
|
||||||
|
reg = <0xa6000000 0x2
|
||||||
|
0xa6000004 0x2
|
||||||
|
0xa6000008 0x2>;
|
||||||
|
reg-io-width = <2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
usb: usb@a7000000 {
|
||||||
|
compatible = "cypress,sl811";
|
||||||
|
reg = <0xa7000000 0x1
|
||||||
|
0xa7000004 0x1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
use once::*;
|
||||||
|
|
||||||
|
pub mod serial;
|
||||||
|
#[path = "../../../../drivers/gpu/fb.rs"]
|
||||||
|
pub mod fb;
|
||||||
|
#[path = "../../../../drivers/console/mod.rs"]
|
||||||
|
pub mod console;
|
||||||
|
|
||||||
|
/// Initialize serial port first
|
||||||
|
pub fn init_serial_early() {
|
||||||
|
assert_has_not_been_called!("board::init must be called only once");
|
||||||
|
serial::init(0xa3000000);
|
||||||
|
println!("Hello ThinPad!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize other board drivers
|
||||||
|
pub fn init_driver() {
|
||||||
|
// TODO: add possibly more drivers
|
||||||
|
// timer::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probe_fb_info(width: u32, height: u32, depth: u32) -> Result<(FramebufferInfo, u32), String> {
|
||||||
|
let fb_info = FramebufferInfo {
|
||||||
|
xres: 800,
|
||||||
|
yres: 600,
|
||||||
|
xres_virtual: 800,
|
||||||
|
yres_virtual: 600,
|
||||||
|
xoffset: 0,
|
||||||
|
yoffset: 0,
|
||||||
|
depth: 8,
|
||||||
|
pitch: 800,
|
||||||
|
bus_addr: 0xa2000000,
|
||||||
|
screen_size: 800 * 600,
|
||||||
|
}
|
||||||
|
Ok((fb_info, 0xa2000000))
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
//! naive serial adapter driver for thinpad
|
||||||
|
|
||||||
|
use core::fmt::{Write, Result, Arguments};
|
||||||
|
use core::ptr::{read_volatile, write_volatile};
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
struct SerialPort {
|
||||||
|
base: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
const UART_STATUS: usize = 0;
|
||||||
|
const UART_DATA: usize = 0;
|
||||||
|
|
||||||
|
const UART_STATUS_CTS: u8 = 0x1; // clear to send signal
|
||||||
|
const UART_STATUS_DR: u8 = 0x2; // data ready signal
|
||||||
|
|
||||||
|
|
||||||
|
impl SerialPort {
|
||||||
|
fn new() -> SerialPort {
|
||||||
|
SerialPort {
|
||||||
|
base: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, base: usize) {
|
||||||
|
self.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of putchar()
|
||||||
|
fn putchar(&mut self, c: u8) {
|
||||||
|
write(self.base + UART_DATA, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// blocking version of getchar()
|
||||||
|
pub fn getchar(&mut self) -> char {
|
||||||
|
loop {
|
||||||
|
if (read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let c = read::<u8>(self.base + UART_DATA);
|
||||||
|
match c {
|
||||||
|
255 => '\0', // null
|
||||||
|
c => c as char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-blocking version of getchar()
|
||||||
|
pub fn getchar_option(&mut self) -> Option<char> {
|
||||||
|
match read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(read::<u8>(self.base + UART_DATA) as u8 as char),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||||
|
self.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for SerialPort {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result {
|
||||||
|
for c in s.bytes() {
|
||||||
|
if c == 127 {
|
||||||
|
self.putchar(8);
|
||||||
|
self.putchar(b' ');
|
||||||
|
self.putchar(8);
|
||||||
|
} else {
|
||||||
|
self.putchar(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<T>(addr: usize, content: T) {
|
||||||
|
let cell = (addr) as *mut T;
|
||||||
|
write_volatile(cell, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<T>(addr: usize) -> T {
|
||||||
|
let cell = (addr) as *const T;
|
||||||
|
read_volatile(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(base: usize) {
|
||||||
|
SERIAL_PORT.lock().init(base);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
//! mipsel drivers
|
||||||
|
|
||||||
|
use super::board;
|
||||||
|
use once::*;
|
||||||
|
|
||||||
|
pub use self::board::serial;
|
||||||
|
pub use self::board::fb;
|
||||||
|
#[path = "../../../drivers/console/mod.rs"]
|
||||||
|
pub mod console;
|
||||||
|
|
||||||
|
/// Initialize common drivers
|
||||||
|
pub fn init() {
|
||||||
|
assert_has_not_been_called!("driver::init must be called only once");
|
||||||
|
board::init_driver();
|
||||||
|
console::init();
|
||||||
|
}
|
@ -1,48 +1,30 @@
|
|||||||
use core::fmt::{Write, Result, Arguments};
|
//! Input/output for mipsel.
|
||||||
|
|
||||||
struct SerialPort;
|
use super::driver::console::CONSOLE;
|
||||||
|
use super::driver::serial::*;
|
||||||
impl Write for SerialPort {
|
use core::fmt::{Arguments, Write};
|
||||||
fn write_str(&mut self, s: &str) -> Result {
|
|
||||||
for c in s.bytes() {
|
|
||||||
if c == 127 {
|
|
||||||
putchar(8);
|
|
||||||
putchar(b' ');
|
|
||||||
putchar(8);
|
|
||||||
} else {
|
|
||||||
putchar(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn putchar(c: u8) {
|
|
||||||
// TODO: output to uart
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getchar() -> char {
|
pub fn getchar() -> char {
|
||||||
// TODO: get char from uart
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
let c = 0 as u8;
|
SERIAL_PORT.lock().getchar()
|
||||||
|
|
||||||
match c {
|
|
||||||
255 => '\0', // null
|
|
||||||
c => c as char,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getchar_option() -> Option<char> {
|
pub fn getchar_option() -> Option<char> {
|
||||||
// TODO: get char from uart
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
let c = 0 as u8;
|
SERIAL_PORT.lock().getchar_option()
|
||||||
match c {
|
|
||||||
-1 => None,
|
|
||||||
c => Some(c as u8 as char),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn putfmt(fmt: Arguments) {
|
pub fn putfmt(fmt: Arguments) {
|
||||||
SerialPort.write_fmt(fmt).unwrap();
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
|
SERIAL_PORT.lock().write_fmt(fmt).unwrap();
|
||||||
|
|
||||||
|
unsafe { CONSOLE.force_unlock() }
|
||||||
|
if let Some(console) = CONSOLE.lock().as_mut() {
|
||||||
|
console.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TXDATA: *mut u32 = 0x38000000 as *mut u32;
|
pub fn putchar(c: u8) {
|
||||||
const RXDATA: *mut u32 = 0x38000004 as *mut u32;
|
unsafe { SERIAL_PORT.force_unlock() }
|
||||||
|
SERIAL_PORT.lock().putchar(c);
|
||||||
|
}
|
||||||
|
@ -0,0 +1,497 @@
|
|||||||
|
//! Driver for AHCI
|
||||||
|
//!
|
||||||
|
//! Spec: https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf
|
||||||
|
|
||||||
|
use alloc::alloc::{alloc_zeroed, Layout};
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::mem::size_of;
|
||||||
|
use core::slice;
|
||||||
|
use core::sync::atomic::spin_loop_hint;
|
||||||
|
|
||||||
|
use bit_field::*;
|
||||||
|
use bitflags::*;
|
||||||
|
use log::*;
|
||||||
|
use rcore_fs::dev::BlockDevice;
|
||||||
|
use volatile::Volatile;
|
||||||
|
|
||||||
|
use rcore_memory::paging::PageTable;
|
||||||
|
use rcore_memory::{PhysAddr, VirtAddr, PAGE_SIZE};
|
||||||
|
|
||||||
|
use crate::drivers::BlockDriver;
|
||||||
|
use crate::memory::active_table;
|
||||||
|
use crate::sync::SpinNoIrqLock as Mutex;
|
||||||
|
|
||||||
|
use super::super::{DeviceType, Driver, BLK_DRIVERS, DRIVERS};
|
||||||
|
|
||||||
|
pub struct AHCI {
|
||||||
|
header: usize,
|
||||||
|
size: usize,
|
||||||
|
received_fis: &'static mut AHCIReceivedFIS,
|
||||||
|
cmd_list: &'static mut [AHCICommandHeader],
|
||||||
|
cmd_table: &'static mut AHCICommandTable,
|
||||||
|
data: &'static mut [u8],
|
||||||
|
port: &'static mut AHCIPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AHCIDriver(Mutex<AHCI>);
|
||||||
|
|
||||||
|
/// AHCI Generic Host Control (3.1)
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCIGHC {
|
||||||
|
/// Host capability
|
||||||
|
capability: Volatile<AHCICap>,
|
||||||
|
/// Global host control
|
||||||
|
global_host_control: Volatile<u32>,
|
||||||
|
/// Interrupt status
|
||||||
|
interrupt_status: Volatile<u32>,
|
||||||
|
/// Port implemented
|
||||||
|
port_implemented: Volatile<u32>,
|
||||||
|
/// Version
|
||||||
|
version: Volatile<u32>,
|
||||||
|
/// Command completion coalescing control
|
||||||
|
ccc_control: Volatile<u32>,
|
||||||
|
/// Command completion coalescing ports
|
||||||
|
ccc_ports: Volatile<u32>,
|
||||||
|
/// Enclosure management location
|
||||||
|
em_location: Volatile<u32>,
|
||||||
|
/// Enclosure management control
|
||||||
|
em_control: Volatile<u32>,
|
||||||
|
/// Host capabilities extended
|
||||||
|
capabilities2: Volatile<u32>,
|
||||||
|
/// BIOS/OS handoff control and status
|
||||||
|
bios_os_handoff_control: Volatile<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
struct AHCICap : u32 {
|
||||||
|
const S64A = 1 << 31;
|
||||||
|
const SNCQ = 1 << 30;
|
||||||
|
const SSNTF = 1 << 29;
|
||||||
|
const SMPS = 1 << 28;
|
||||||
|
const SSS = 1 << 27;
|
||||||
|
const SALP = 1 << 26;
|
||||||
|
const SAL = 1 << 25;
|
||||||
|
const SCLO = 1 << 24;
|
||||||
|
const ISS_GEN_1 = 1 << 20;
|
||||||
|
const ISS_GEN_2 = 2 << 20;
|
||||||
|
const ISS_GEN_3 = 3 << 20;
|
||||||
|
const SAM = 1 << 18;
|
||||||
|
const SPM = 1 << 17;
|
||||||
|
const FBSS = 1 << 16;
|
||||||
|
const PMD = 1 << 15;
|
||||||
|
const SSC = 1 << 14;
|
||||||
|
const PSC = 1 << 13;
|
||||||
|
const CCCS = 1 << 7;
|
||||||
|
const EMS = 1 << 6;
|
||||||
|
const SXS = 1 << 5;
|
||||||
|
// number of ports - 1
|
||||||
|
const NUM_MASK = 0b11111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AHCIGHC {
|
||||||
|
fn enable(&mut self) {
|
||||||
|
self.global_host_control.update(|v| {
|
||||||
|
v.set_bit(13, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn num_ports(&self) -> usize {
|
||||||
|
(self.capability.read() & AHCICap::NUM_MASK).bits() as usize + 1
|
||||||
|
}
|
||||||
|
fn has_port(&self, port_num: usize) -> bool {
|
||||||
|
self.port_implemented.read().get_bit(port_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHCI Port Registers (3.3) (one set per port)
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCIPort {
|
||||||
|
command_list_base_address: Volatile<u64>,
|
||||||
|
fis_base_address: Volatile<u64>,
|
||||||
|
interrupt_status: Volatile<u32>,
|
||||||
|
interrupt_enable: Volatile<u32>,
|
||||||
|
command: Volatile<u32>,
|
||||||
|
reserved: Volatile<u32>,
|
||||||
|
task_file_data: Volatile<u32>,
|
||||||
|
signature: Volatile<u32>,
|
||||||
|
sata_status: Volatile<u32>,
|
||||||
|
sata_control: Volatile<u32>,
|
||||||
|
sata_error: Volatile<u32>,
|
||||||
|
sata_active: Volatile<u32>,
|
||||||
|
command_issue: Volatile<u32>,
|
||||||
|
sata_notification: Volatile<u32>,
|
||||||
|
fis_based_switch_control: Volatile<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AHCIPort {
|
||||||
|
fn spin_on_slot(&mut self, slot: usize) {
|
||||||
|
loop {
|
||||||
|
let ci = self.command_issue.read();
|
||||||
|
if !ci.get_bit(slot) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_loop_hint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn issue_command(&mut self, slot: usize) {
|
||||||
|
assert!(slot < 32);
|
||||||
|
self.command_issue.write(1 << (slot as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHCI Received FIS Structure (4.2.1)
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCIReceivedFIS {
|
||||||
|
dma: [u8; 0x20],
|
||||||
|
pio: [u8; 0x20],
|
||||||
|
d2h: [u8; 0x18],
|
||||||
|
sdbfis: [u8; 0x8],
|
||||||
|
ufis: [u8; 0x40],
|
||||||
|
reserved: [u8; 0x60],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # AHCI Command List Structure (4.2.2)
|
||||||
|
///
|
||||||
|
/// Host sends commands to the device through Command List.
|
||||||
|
///
|
||||||
|
/// Command List consists of 1 to 32 command headers, each one is called a slot.
|
||||||
|
///
|
||||||
|
/// Each command header describes an ATA or ATAPI command, including a
|
||||||
|
/// Command FIS, an ATAPI command buffer and a bunch of Physical Region
|
||||||
|
/// Descriptor Tables specifying the data payload address and size.
|
||||||
|
///
|
||||||
|
/// https://wiki.osdev.org/images/e/e8/Command_list.jpg
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCICommandHeader {
|
||||||
|
///
|
||||||
|
flags: CommandHeaderFlags,
|
||||||
|
/// Physical region descriptor table length in entries
|
||||||
|
prdt_length: u16,
|
||||||
|
/// Physical region descriptor byte count transferred
|
||||||
|
prd_byte_count: u32,
|
||||||
|
/// Command table descriptor base address
|
||||||
|
command_table_base_address: u64,
|
||||||
|
/// Reserved
|
||||||
|
reserved: [u32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct CommandHeaderFlags: u16 {
|
||||||
|
/// Command FIS length in DWORDS, 2 ~ 16
|
||||||
|
const CFL_MASK = 0b11111;
|
||||||
|
/// ATAPI
|
||||||
|
const ATAPI = 1 << 5;
|
||||||
|
/// Write, 1: H2D, 0: D2H
|
||||||
|
const WRITE = 1 << 6;
|
||||||
|
/// Prefetchable
|
||||||
|
const PREFETCHABLE = 1 << 7;
|
||||||
|
/// Reset
|
||||||
|
const RESET = 1 << 8;
|
||||||
|
/// BIST
|
||||||
|
const BIST = 1 << 9;
|
||||||
|
/// Clear busy upon R_OK
|
||||||
|
const CLEAR = 1 << 10;
|
||||||
|
/// Port multiplier port
|
||||||
|
const PORT_MULTIPLIER_PORT_MASK = 0b1111 << 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHCI Command Table (4.2.3)
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCICommandTable {
|
||||||
|
/// Command FIS
|
||||||
|
cfis: SATAFISRegH2D,
|
||||||
|
/// ATAPI command, 12 or 16 bytes
|
||||||
|
acmd: [u8; 16],
|
||||||
|
/// Reserved
|
||||||
|
reserved: [u8; 48],
|
||||||
|
/// Physical region descriptor table entries, 0 ~ 65535
|
||||||
|
prdt: [AHCIPrdtEntry; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Physical region descriptor table entry
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AHCIPrdtEntry {
|
||||||
|
/// Data base address
|
||||||
|
data_base_address: u64,
|
||||||
|
/// Reserved
|
||||||
|
reserved: u32,
|
||||||
|
/// Bit 21-0: Byte count, 4M max
|
||||||
|
/// Bit 31: Interrupt on completion
|
||||||
|
dbc_i: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIS_REG_H2D: u8 = 0x27;
|
||||||
|
|
||||||
|
const CMD_READ_DMA_EXT: u8 = 0x25;
|
||||||
|
const CMD_WRITE_DMA_EXT: u8 = 0x35;
|
||||||
|
const CMD_IDENTIFY_DEVICE: u8 = 0xec;
|
||||||
|
|
||||||
|
/// SATA Register FIS - Host to Device
|
||||||
|
///
|
||||||
|
/// https://wiki.osdev.org/AHCI Figure 5-2
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SATAFISRegH2D {
|
||||||
|
fis_type: u8,
|
||||||
|
cflags: u8,
|
||||||
|
command: u8,
|
||||||
|
feature_lo: u8,
|
||||||
|
|
||||||
|
lba_0: u8, // LBA 7:0
|
||||||
|
lba_1: u8, // LBA 15:8
|
||||||
|
lba_2: u8, // LBA 23:16
|
||||||
|
dev_head: u8,
|
||||||
|
|
||||||
|
lba_3: u8, // LBA 31:24
|
||||||
|
lba_4: u8, // LBA 39:32
|
||||||
|
lba_5: u8, // LBA 47:40
|
||||||
|
feature_hi: u8,
|
||||||
|
|
||||||
|
sector_count: u16,
|
||||||
|
reserved: u8,
|
||||||
|
control: u8,
|
||||||
|
|
||||||
|
_padding: [u8; 48],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SATAFISRegH2D {
|
||||||
|
fn set_lba(&mut self, lba: u64) {
|
||||||
|
self.lba_0 = (lba >> 0) as u8;
|
||||||
|
self.lba_1 = (lba >> 8) as u8;
|
||||||
|
self.lba_2 = (lba >> 16) as u8;
|
||||||
|
self.lba_3 = (lba >> 24) as u8;
|
||||||
|
self.lba_4 = (lba >> 32) as u8;
|
||||||
|
self.lba_5 = (lba >> 40) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IDENTIFY DEVICE data
|
||||||
|
///
|
||||||
|
/// ATA8-ACS Table 29
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ATAIdentifyPacket {
|
||||||
|
_1: [u16; 10],
|
||||||
|
serial: [u8; 20], // words 10-19
|
||||||
|
_2: [u16; 3],
|
||||||
|
firmware: [u8; 8], // words 23-26
|
||||||
|
model: [u8; 40], // words 27-46
|
||||||
|
_3: [u16; 13],
|
||||||
|
lba_sectors: u32, // words 60-61
|
||||||
|
_4: [u16; 38],
|
||||||
|
lba48_sectors: u64, // words 100-103
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AHCI {
|
||||||
|
fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> usize {
|
||||||
|
self.cmd_list[0].flags = CommandHeaderFlags::empty();
|
||||||
|
|
||||||
|
let fis = &mut self.cmd_table.cfis;
|
||||||
|
// Register FIS from HBA to device
|
||||||
|
fis.fis_type = FIS_REG_H2D;
|
||||||
|
fis.cflags = 1 << 7;
|
||||||
|
// 7.25 READ DMA EXT - 25h, DMA
|
||||||
|
fis.command = CMD_READ_DMA_EXT;
|
||||||
|
fis.sector_count = 1;
|
||||||
|
fis.dev_head = 0x40; // LBA
|
||||||
|
fis.control = 0x80; // LBA48
|
||||||
|
fis.set_lba(block_id as u64);
|
||||||
|
|
||||||
|
self.port.issue_command(0);
|
||||||
|
self.port.spin_on_slot(0);
|
||||||
|
|
||||||
|
let len = buf.len().min(BLOCK_SIZE);
|
||||||
|
buf[..len].clone_from_slice(&self.data[0..len]);
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_block(&mut self, block_id: usize, buf: &[u8]) -> usize {
|
||||||
|
self.cmd_list[0].flags = CommandHeaderFlags::WRITE; // device write
|
||||||
|
|
||||||
|
let len = buf.len().min(BLOCK_SIZE);
|
||||||
|
self.data[0..len].clone_from_slice(&buf[..len]);
|
||||||
|
|
||||||
|
let fis = &mut self.cmd_table.cfis;
|
||||||
|
// Register FIS from HBA to device
|
||||||
|
fis.fis_type = FIS_REG_H2D;
|
||||||
|
fis.cflags = 1 << 7;
|
||||||
|
// ATA8-ACS
|
||||||
|
// 7.63 WRITE DMA EXT - 35h, DMA
|
||||||
|
fis.command = CMD_WRITE_DMA_EXT;
|
||||||
|
fis.sector_count = 1;
|
||||||
|
fis.dev_head = 0x40; // LBA
|
||||||
|
fis.control = 0x80; // LBA48
|
||||||
|
fis.set_lba(block_id as u64);
|
||||||
|
|
||||||
|
self.port.issue_command(0);
|
||||||
|
self.port.spin_on_slot(0);
|
||||||
|
|
||||||
|
len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for AHCIDriver {
|
||||||
|
fn try_handle_interrupt(&self, _irq: Option<u32>) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_type(&self) -> DeviceType {
|
||||||
|
DeviceType::Block
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
format!("ahci")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_block(&self, block_id: usize, buf: &mut [u8]) -> bool {
|
||||||
|
let mut driver = self.0.lock();
|
||||||
|
driver.read_block(block_id, buf);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_block(&self, block_id: usize, buf: &[u8]) -> bool {
|
||||||
|
if buf.len() < BLOCK_SIZE {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut driver = self.0.lock();
|
||||||
|
driver.write_block(block_id, buf);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLOCK_SIZE: usize = 512;
|
||||||
|
|
||||||
|
fn from_ata_string(data: &[u8]) -> String {
|
||||||
|
let mut swapped_data = Vec::new();
|
||||||
|
assert_eq!(data.len() % 2, 0);
|
||||||
|
for i in (0..data.len()).step_by(2) {
|
||||||
|
swapped_data.push(data[i + 1]);
|
||||||
|
swapped_data.push(data[i]);
|
||||||
|
}
|
||||||
|
return String::from_utf8(swapped_data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate consequent physical frames for DMA
|
||||||
|
fn alloc_dma(page_num: usize) -> (VirtAddr, PhysAddr) {
|
||||||
|
let layout = Layout::from_size_align(PAGE_SIZE * page_num, PAGE_SIZE).unwrap();
|
||||||
|
let vaddr = unsafe { alloc_zeroed(layout) } as usize;
|
||||||
|
let paddr = active_table().get_entry(vaddr).unwrap().target();
|
||||||
|
(vaddr, paddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ahci_init(irq: Option<u32>, header: usize, size: usize) -> Arc<AHCIDriver> {
|
||||||
|
let ghc = unsafe { &mut *(header as *mut AHCIGHC) };
|
||||||
|
|
||||||
|
ghc.enable();
|
||||||
|
|
||||||
|
for port_num in 0..ghc.num_ports() {
|
||||||
|
if ghc.has_port(port_num) {
|
||||||
|
let addr = header + 0x100 + 0x80 * port_num;
|
||||||
|
let port = unsafe { &mut *(addr as *mut AHCIPort) };
|
||||||
|
|
||||||
|
// SSTS IPM Active
|
||||||
|
if port.sata_status.read().get_bits(8..12) != 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSTS DET Present
|
||||||
|
if port.sata_status.read().get_bits(0..4) != 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("probing port {}", port_num);
|
||||||
|
// Disable Port First
|
||||||
|
port.command.update(|c| {
|
||||||
|
c.set_bit(4, false);
|
||||||
|
c.set_bit(0, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
let (rfis_va, rfis_pa) = alloc_dma(1);
|
||||||
|
let (cmd_list_va, cmd_list_pa) = alloc_dma(1);
|
||||||
|
let (cmd_table_va, cmd_table_pa) = alloc_dma(1);
|
||||||
|
let (data_va, data_pa) = alloc_dma(1);
|
||||||
|
|
||||||
|
let received_fis = unsafe { &mut *(rfis_va as *mut AHCIReceivedFIS) };
|
||||||
|
let cmd_list = unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
cmd_list_va as *mut AHCICommandHeader,
|
||||||
|
PAGE_SIZE / size_of::<AHCICommandHeader>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let cmd_table = unsafe { &mut *(cmd_table_va as *mut AHCICommandTable) };
|
||||||
|
let identify_data = unsafe { &*(data_va as *mut ATAIdentifyPacket) };
|
||||||
|
|
||||||
|
cmd_table.prdt[0].data_base_address = data_pa as u64;
|
||||||
|
cmd_table.prdt[0].dbc_i = (BLOCK_SIZE - 1) as u32;
|
||||||
|
|
||||||
|
cmd_list[0].command_table_base_address = cmd_table_pa as u64;
|
||||||
|
cmd_list[0].prdt_length = 1;
|
||||||
|
cmd_list[0].prd_byte_count = 0;
|
||||||
|
|
||||||
|
port.command_list_base_address.write(cmd_list_pa as u64);
|
||||||
|
port.fis_base_address.write(rfis_pa as u64);
|
||||||
|
|
||||||
|
// clear status and errors
|
||||||
|
port.command_issue.write(0);
|
||||||
|
port.sata_active.write(0);
|
||||||
|
port.sata_error.write(0);
|
||||||
|
|
||||||
|
// enable port
|
||||||
|
port.command.update(|c| {
|
||||||
|
*c |= 1 << 0 | 1 << 1 | 1 << 2 | 1 << 4 | 1 << 28;
|
||||||
|
});
|
||||||
|
|
||||||
|
let stat = port.sata_status.read();
|
||||||
|
if stat == 0 {
|
||||||
|
warn!("port is not connected to external drive?");
|
||||||
|
}
|
||||||
|
|
||||||
|
let fis = &mut cmd_table.cfis;
|
||||||
|
// Register FIS from HBA to device
|
||||||
|
fis.fis_type = FIS_REG_H2D;
|
||||||
|
fis.cflags = 1 << 7;
|
||||||
|
|
||||||
|
// 7.15 IDENTIFY DEVICE - ECh, PIO Data-In
|
||||||
|
fis.command = CMD_IDENTIFY_DEVICE;
|
||||||
|
fis.sector_count = 1;
|
||||||
|
|
||||||
|
port.issue_command(0);
|
||||||
|
port.spin_on_slot(0);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
debug!(
|
||||||
|
"Found ATA Device serial {} firmware {} model {} sectors 24bit={} 48bit={}",
|
||||||
|
from_ata_string(&identify_data.serial).trim_end(),
|
||||||
|
from_ata_string(&identify_data.firmware).trim_end(),
|
||||||
|
from_ata_string(&identify_data.model).trim_end(),
|
||||||
|
identify_data.lba_sectors,
|
||||||
|
identify_data.lba48_sectors,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = unsafe { slice::from_raw_parts_mut(data_va as *mut u8, BLOCK_SIZE) };
|
||||||
|
|
||||||
|
let driver = AHCIDriver(Mutex::new(AHCI {
|
||||||
|
header,
|
||||||
|
size,
|
||||||
|
received_fis,
|
||||||
|
cmd_list,
|
||||||
|
cmd_table,
|
||||||
|
data,
|
||||||
|
port,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let driver = Arc::new(driver);
|
||||||
|
DRIVERS.write().push(driver.clone());
|
||||||
|
BLK_DRIVERS
|
||||||
|
.write()
|
||||||
|
.push(Arc::new(BlockDriver(driver.clone())));
|
||||||
|
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unimplemented!();
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
|
pub mod ahci;
|
||||||
pub mod virtio_blk;
|
pub mod virtio_blk;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 69febc9fcc64df60329687b4f24b9b5309c99adf
|
Subproject commit 97bae0c39a7aeaab07654d99c3ba3bcb4d01658c
|
Loading…
Reference in new issue