forked from koniifer/ableos
Merge pull request 'IDE disk driver using DMA' (#27) from theoddgarlic/ableos:ata into master
Reviewed-on: https://git.ablecorp.us:443/able/ableos/pulls/27
This commit is contained in:
commit
50410f4dc1
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -54,6 +54,7 @@ dependencies = [
|
||||||
"rdrand",
|
"rdrand",
|
||||||
"riscv",
|
"riscv",
|
||||||
"rkyv",
|
"rkyv",
|
||||||
|
"seq-macro",
|
||||||
"serde",
|
"serde",
|
||||||
"spin 0.9.4",
|
"spin 0.9.4",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -65,7 +66,6 @@ dependencies = [
|
||||||
"wasmi",
|
"wasmi",
|
||||||
"watson",
|
"watson",
|
||||||
"x86_64",
|
"x86_64",
|
||||||
"x86_ata",
|
|
||||||
"y-compositor-protocol",
|
"y-compositor-protocol",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -649,6 +649,12 @@ version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seq-macro"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0772c5c30e1a0d91f6834f8e545c69281c099dfa9a3ac58d96a9fd629c8d4898"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.141"
|
version = "1.0.141"
|
||||||
|
@ -879,18 +885,6 @@ dependencies = [
|
||||||
"volatile 0.4.5",
|
"volatile 0.4.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "x86_ata"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
"serde",
|
|
||||||
"spin 0.9.4",
|
|
||||||
"x86_64",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "y-compositor-protocol"
|
name = "y-compositor-protocol"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
|
@ -8,6 +8,18 @@ version = "0.1.1"
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[package.metadata.bootimage]
|
[package.metadata.bootimage]
|
||||||
|
run-command = [
|
||||||
|
"qemu-system-x86_64",
|
||||||
|
|
||||||
|
"-device",
|
||||||
|
"piix4-ide,id=ide",
|
||||||
|
|
||||||
|
"-drive",
|
||||||
|
"file={},format=raw,if=none,id=disk",
|
||||||
|
|
||||||
|
"-device",
|
||||||
|
"ide-hd,drive=disk,bus=ide.0"
|
||||||
|
]
|
||||||
run-args = [
|
run-args = [
|
||||||
# "--nodefaults",
|
# "--nodefaults",
|
||||||
"-cpu",
|
"-cpu",
|
||||||
|
@ -38,14 +50,8 @@ run-args = [
|
||||||
|
|
||||||
# "-machine", "pcspk-audiodev=0",
|
# "-machine", "pcspk-audiodev=0",
|
||||||
|
|
||||||
|
|
||||||
"-hdb",
|
|
||||||
"../userland/root_fs/ext2.img",
|
|
||||||
|
|
||||||
|
|
||||||
# "-qmp",
|
# "-qmp",
|
||||||
# "unix:../qmp-sock,server,nowait",
|
# "unix:../qmp-sock,server,nowait",
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
test-args = [
|
test-args = [
|
||||||
|
@ -72,7 +78,7 @@ versioning = { git = "https://git.ablecorp.us/able/aos_userland" }
|
||||||
pc-keyboard = "0.5"
|
pc-keyboard = "0.5"
|
||||||
# mini-backtrace = "0.1"
|
# mini-backtrace = "0.1"
|
||||||
clparse = { git = "https://git.ablecorp.us/able/core_utils", default-features = false }
|
clparse = { git = "https://git.ablecorp.us/able/core_utils", default-features = false }
|
||||||
|
seq-macro = "0.3"
|
||||||
|
|
||||||
[dependencies.linked_list_allocator]
|
[dependencies.linked_list_allocator]
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -148,7 +154,6 @@ git = "https://git.ablecorp.us:443/able/externc-libm.git"
|
||||||
riscv = "*"
|
riscv = "*"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
x86_ata = { path = "../x86_ata" }
|
|
||||||
bootloader = { version = "0.9.8", features = ["map_physical_memory"] }
|
bootloader = { version = "0.9.8", features = ["map_physical_memory"] }
|
||||||
cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" }
|
cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" }
|
||||||
pic8259 = "0.10.1"
|
pic8259 = "0.10.1"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ use crate::{arch::gdt, println, rhai_shell::KEYBUFF};
|
||||||
use cpuio::outb;
|
use cpuio::outb;
|
||||||
use pic8259::ChainedPics;
|
use pic8259::ChainedPics;
|
||||||
use qrcode::QrCode;
|
use qrcode::QrCode;
|
||||||
|
use seq_macro::seq;
|
||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||||
|
|
||||||
|
@ -30,6 +31,9 @@ pub enum InterruptIndex {
|
||||||
/// Mouse offset
|
/// Mouse offset
|
||||||
Mouse = 44,
|
Mouse = 44,
|
||||||
|
|
||||||
|
/// Disk offset
|
||||||
|
Disk = 46,
|
||||||
|
|
||||||
// SecondInterrupt = PIC_2_OFFSET,
|
// SecondInterrupt = PIC_2_OFFSET,
|
||||||
Cmos = 0x70,
|
Cmos = 0x70,
|
||||||
}
|
}
|
||||||
|
@ -47,9 +51,9 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
|
||||||
reset_pit_for_cpu();
|
reset_pit_for_cpu();
|
||||||
let mut idt = InterruptDescriptorTable::new();
|
let mut idt = InterruptDescriptorTable::new();
|
||||||
|
|
||||||
for int in 32..=255 {
|
seq!(N in 32..=255 {
|
||||||
idt[int].set_handler_fn(undefined_handler);
|
idt[N].set_handler_fn(undefined_handler_~N);
|
||||||
}
|
});
|
||||||
|
|
||||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -68,12 +72,21 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
|
||||||
idt
|
idt
|
||||||
});
|
});
|
||||||
|
|
||||||
extern "x86-interrupt" fn undefined_handler(stack_frame: InterruptStackFrame) {
|
seq!(N in 32..=255 {
|
||||||
error!("{:?}", stack_frame);
|
extern "x86-interrupt" fn undefined_handler_~N(stack_frame: InterruptStackFrame) {
|
||||||
}
|
error!("INT {}: {:?}", N, stack_frame);
|
||||||
|
unsafe {
|
||||||
|
PICS.lock()
|
||||||
|
.notify_end_of_interrupt(N);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
extern "x86-interrupt" fn software_int_handler(stack_frame: InterruptStackFrame) {
|
extern "x86-interrupt" fn software_int_handler(stack_frame: InterruptStackFrame) {
|
||||||
trace!("EXCEPTION: SOFTWARE INT\n{:#?}", stack_frame);
|
trace!("EXCEPTION: SOFTWARE INT\n{:#?}", stack_frame);
|
||||||
|
unsafe {
|
||||||
|
PICS.lock().notify_end_of_interrupt(54);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
|
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
|
||||||
use x86_64::{
|
use x86_64::{
|
||||||
structures::paging::{
|
structures::paging::{
|
||||||
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
|
FrameAllocator, FrameDeallocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame,
|
||||||
|
Size4KiB,
|
||||||
},
|
},
|
||||||
PhysAddr, VirtAddr,
|
PhysAddr, VirtAddr,
|
||||||
};
|
};
|
||||||
|
@ -109,3 +110,9 @@ unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FrameDeallocator<Size4KiB> for BootInfoFrameAllocator {
|
||||||
|
unsafe fn deallocate_frame(&mut self, _frame: PhysFrame<Size4KiB>) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod drivers;
|
pub mod drivers;
|
||||||
pub mod gdt;
|
pub mod gdt;
|
||||||
|
@ -34,7 +34,7 @@ pub fn start(boot_info: &'static BootInfo) -> ! {
|
||||||
|
|
||||||
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
|
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
|
||||||
|
|
||||||
crate::kmain::kernel_main();
|
crate::kmain::kernel_main(mapper, frame_allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::CharacterDevice;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct DevNull;
|
pub struct DevNull;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::CharacterDevice;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DevUnicode {
|
pub struct DevUnicode {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::CharacterDevice;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DevZero;
|
pub struct DevZero;
|
||||||
|
|
|
@ -2,4 +2,4 @@ pub mod dev_null;
|
||||||
pub mod dev_unicode;
|
pub mod dev_unicode;
|
||||||
pub mod dev_zero;
|
pub mod dev_zero;
|
||||||
|
|
||||||
pub use kernel::device_interface::character::CharacterDevice;
|
pub use kernel::device_interface::CharacterDevice;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use core::ops::Not;
|
use core::ops::Not;
|
||||||
use core::sync::atomic::AtomicU32;
|
use core::sync::atomic::AtomicU32;
|
||||||
use core::sync::atomic::Ordering;
|
use core::sync::atomic::Ordering;
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::CharacterDevice;
|
||||||
|
|
||||||
use crate::pixel_format::Rgba64;
|
use crate::pixel_format::Rgba64;
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,13 @@ mod dev_vterm;
|
||||||
|
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod pci;
|
pub mod pci;
|
||||||
pub mod pci_inner;
|
|
||||||
|
|
||||||
pub use self::Device::*;
|
pub use self::Device::*;
|
||||||
|
|
||||||
use crate::devices::dev_vterm::VTerm;
|
use crate::devices::dev_vterm::VTerm;
|
||||||
use character_devs::{dev_null::DevNull, dev_unicode::DevUnicode, dev_zero::DevZero};
|
use character_devs::{dev_null::DevNull, dev_unicode::DevUnicode, dev_zero::DevZero};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::{BlockDevice, CharacterDevice};
|
||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
|
|
||||||
pub static DEVICE_TABLE: Lazy<spin::Mutex<DeviceTable>> =
|
pub static DEVICE_TABLE: Lazy<spin::Mutex<DeviceTable>> =
|
||||||
|
@ -19,6 +18,7 @@ pub static DEVICE_TABLE: Lazy<spin::Mutex<DeviceTable>> =
|
||||||
// FIXME: This is a hack to hold a device.
|
// FIXME: This is a hack to hold a device.
|
||||||
// #[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub enum Device {
|
pub enum Device {
|
||||||
|
Block(Box<dyn BlockDevice>),
|
||||||
Character(Box<dyn CharacterDevice>),
|
Character(Box<dyn CharacterDevice>),
|
||||||
Vterm(Box<VTerm>),
|
Vterm(Box<VTerm>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
*
|
||||||
*/
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
/// The major class specification for a PCI device.
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
#[allow(dead_code)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
/// Class specification for a PCI device
|
||||||
pub enum PciClass {
|
pub enum PciClass {
|
||||||
Unclassified = 0x00,
|
Unclassified = 0x00,
|
||||||
MassStorage = 0x01,
|
MassStorage = 0x01,
|
||||||
|
@ -19,37 +19,28 @@ pub enum PciClass {
|
||||||
Multimedia = 0x04,
|
Multimedia = 0x04,
|
||||||
Memory = 0x05,
|
Memory = 0x05,
|
||||||
Bridge = 0x06,
|
Bridge = 0x06,
|
||||||
Other = 0xFF,
|
Unknown = 0xFF,
|
||||||
}
|
|
||||||
impl PciClass {
|
|
||||||
/// Convert a u8 into the corresponding PciClass
|
|
||||||
pub fn from_u8(n: u8) -> PciClass {
|
|
||||||
match n {
|
|
||||||
0x00 => PciClass::Unclassified,
|
|
||||||
0x01 => PciClass::MassStorage,
|
|
||||||
0x02 => PciClass::Network,
|
|
||||||
0x03 => PciClass::Display,
|
|
||||||
0x04 => PciClass::Multimedia,
|
|
||||||
0x05 => PciClass::Memory,
|
|
||||||
0x06 => PciClass::Bridge,
|
|
||||||
_ => PciClass::Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Convert a PciClass to its u8 representation
|
|
||||||
pub fn as_u8(&self) -> u8 {
|
|
||||||
*self as u8
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for PciClass {
|
impl From<u8> for PciClass {
|
||||||
/// Convert a u8 into the corresponding PciClass
|
/// Convert a u8 into the corresponding PciClass
|
||||||
fn from(n: u8) -> Self {
|
fn from(n: u8) -> Self {
|
||||||
Self::from_u8(n)
|
use PciClass::*;
|
||||||
|
match n {
|
||||||
|
0x00 => Unclassified,
|
||||||
|
0x01 => MassStorage,
|
||||||
|
0x02 => Network,
|
||||||
|
0x03 => Display,
|
||||||
|
0x04 => Multimedia,
|
||||||
|
0x05 => Memory,
|
||||||
|
0x06 => Bridge,
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
/// Full class specification (type and subtype) for a PCI device.
|
/// Full class specification (type and subtype) for a PCI device.
|
||||||
///
|
///
|
||||||
|
@ -110,6 +101,7 @@ pub enum PciFullClass {
|
||||||
|
|
||||||
Unknown = 0xFFFF,
|
Unknown = 0xFFFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciFullClass {
|
impl PciFullClass {
|
||||||
// listen, i know this sucks, but i didn't want to include
|
// listen, i know this sucks, but i didn't want to include
|
||||||
// `num`, `num-traits` and `num-derive` as dependencies for
|
// `num`, `num-traits` and `num-derive` as dependencies for
|
||||||
|
@ -173,11 +165,13 @@ impl PciFullClass {
|
||||||
_ => PciFullClass::Unknown,
|
_ => PciFullClass::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a PciFullClass to its u16 representation
|
/// Convert a PciFullClass to its u16 representation
|
||||||
pub fn as_u16(&self) -> u16 {
|
pub fn as_u16(&self) -> u16 {
|
||||||
*self as u16
|
*self as u16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u16> for PciFullClass {
|
impl From<u16> for PciFullClass {
|
||||||
/// Convert a u16 into the corresponding PciFullClass
|
/// Convert a u16 into the corresponding PciFullClass
|
||||||
fn from(n: u16) -> Self {
|
fn from(n: u16) -> Self {
|
194
ableos/src/devices/pci/device.rs
Normal file
194
ableos/src/devices/pci/device.rs
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
vendors::Vendor::{self, *},
|
||||||
|
PciClass, PciFullClass,
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Unknown class
|
||||||
|
pub const S3_TRIO64V2: DeviceID = DeviceID::new(S3Inc, 0x8900);
|
||||||
|
|
||||||
|
// MassStorage_IDE (0x0101)
|
||||||
|
pub const INTEL_PIIX3_IDE: DeviceID = DeviceID::new(IntelCorp, 0x7010);
|
||||||
|
pub const INTEL_PIIX4_IDE: DeviceID = DeviceID::new(IntelCorp, 0x7111);
|
||||||
|
|
||||||
|
// Display_VGA (0x0300)
|
||||||
|
pub const VMWARE_SVGA2: DeviceID = DeviceID::new(VMWareInc, 0x0405);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
/// A struct containing info about a PCI device.
|
||||||
|
pub struct PciDeviceInfo {
|
||||||
|
pub header_type: u8,
|
||||||
|
pub device: u8,
|
||||||
|
pub bus: u8,
|
||||||
|
pub device_id: DeviceID,
|
||||||
|
pub full_class: PciFullClass,
|
||||||
|
pub rev_id: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciDeviceInfo {
|
||||||
|
/// Get the class of the PCI device as a PciClass
|
||||||
|
pub fn class(&self) -> PciClass {
|
||||||
|
(((self.full_class.as_u16() >> 8) & 0xFF) as u8).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the bar, 0-indexed
|
||||||
|
pub fn bar(&self, bar: u8) -> u32 {
|
||||||
|
assert!(bar < 6);
|
||||||
|
unsafe { self.read(0, 0x10 + bar * 4) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the interrupt pin
|
||||||
|
pub fn interrupt_pin(&self) -> u8 {
|
||||||
|
let last_row = unsafe { self.read(0, 0x3C) };
|
||||||
|
((last_row >> 8) & 0xFF) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable bus mastering. This allows the PCI device to do DMA
|
||||||
|
pub fn enable_bus_mastering(&self) {
|
||||||
|
let command = unsafe { self.read(0, 4) } | 1 << 2;
|
||||||
|
unsafe { self.write(0, 4, command) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from configuration space
|
||||||
|
pub unsafe fn read(&self, func: u8, offset: u8) -> u32 {
|
||||||
|
pci_config_read(self.bus, self.device, func, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to IO space
|
||||||
|
pub unsafe fn write(&self, func: u8, offset: u8, value: u32) {
|
||||||
|
pci_config_write(self.bus, self.device, func, offset, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PciDeviceInfo {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
let vendor_name = &self.device_id.vendor;
|
||||||
|
let device_id = &self.device_id.id;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Device: {} | Bus: 0x{:X} | Vendor: {} | Device ID: 0x{:X}",
|
||||||
|
self.device, self.bus, vendor_name, device_id,
|
||||||
|
)?;
|
||||||
|
writeln!(f, "{}", self.full_class)?;
|
||||||
|
writeln!(f, " Header type: 0x{:X}", self.header_type)?;
|
||||||
|
writeln!(f, " Revision ID: {}", self.rev_id)?;
|
||||||
|
// write!(f, " Supported functions: 0")?;
|
||||||
|
// for (i, b) in self.supported_fns.iter().enumerate().skip(1) {
|
||||||
|
// if *b {
|
||||||
|
// write!(f, ", {}", i)?;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// writeln!(f)?;
|
||||||
|
// write!(f, " BARs: [ ")?;
|
||||||
|
// for i in self.bars.iter() {
|
||||||
|
// if *i == 0 {
|
||||||
|
// write!(f, "0x0 ")?;
|
||||||
|
// } else {
|
||||||
|
// write!(f, "{:#010X} ", i)?;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// writeln!(f, "]")?;
|
||||||
|
// writeln!(
|
||||||
|
// f,
|
||||||
|
// " Interrupt line / pin: {} / {}",
|
||||||
|
// self.interrupt_line, self.interrupt_pin
|
||||||
|
// )?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct DeviceID {
|
||||||
|
pub vendor: Vendor,
|
||||||
|
pub id: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceID {
|
||||||
|
pub const fn new(vendor: Vendor, id: u16) -> Self {
|
||||||
|
Self { vendor, id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
|
||||||
|
assert!(device < 32);
|
||||||
|
let (device_id, vendor_id) = get_ids(bus, device, 0);
|
||||||
|
if vendor_id == 0xFFFF {
|
||||||
|
// Device doesn't exist
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) };
|
||||||
|
let class = ((reg2 >> 16) & 0x0000FFFF) as u16;
|
||||||
|
let pci_class = PciFullClass::from_u16(class);
|
||||||
|
let header_type = get_header_type(bus, device, 0);
|
||||||
|
|
||||||
|
Some(PciDeviceInfo {
|
||||||
|
header_type,
|
||||||
|
device,
|
||||||
|
bus,
|
||||||
|
device_id: DeviceID {
|
||||||
|
vendor: vendor_id.into(),
|
||||||
|
id: device_id,
|
||||||
|
},
|
||||||
|
full_class: pci_class,
|
||||||
|
rev_id: (reg2 & 0x000000FF) as u8,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
|
||||||
|
let bus = bus as u32;
|
||||||
|
let device = device as u32;
|
||||||
|
let func = func as u32;
|
||||||
|
let offset = offset as u32;
|
||||||
|
// construct address param
|
||||||
|
let address =
|
||||||
|
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
|
||||||
|
|
||||||
|
// write address
|
||||||
|
Port::new(0xCF8).write(address);
|
||||||
|
|
||||||
|
// read data
|
||||||
|
Port::new(0xCFC).read()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
|
||||||
|
let bus = bus as u32;
|
||||||
|
let device = device as u32;
|
||||||
|
let func = func as u32;
|
||||||
|
let offset = offset as u32;
|
||||||
|
// construct address param
|
||||||
|
let address =
|
||||||
|
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
|
||||||
|
|
||||||
|
// write address
|
||||||
|
Port::new(0xCF8).write(address);
|
||||||
|
|
||||||
|
// write data
|
||||||
|
Port::new(0xCFC).write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
|
||||||
|
assert!(device < 32);
|
||||||
|
assert!(function < 8);
|
||||||
|
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
|
||||||
|
((res >> 16) & 0xFF) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
|
||||||
|
assert!(device < 32);
|
||||||
|
assert!(function < 8);
|
||||||
|
let res = unsafe { pci_config_read(bus, device, function, 0) };
|
||||||
|
let dev_id = ((res >> 16) & 0xFFFF) as u16;
|
||||||
|
let vnd_id = (res & 0xFFFF) as u16;
|
||||||
|
(dev_id, vnd_id)
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::vendors::Vendor::{self, *};
|
|
||||||
#[derive(PartialEq, Clone, Eq, Debug)]
|
|
||||||
pub struct DeviceID {
|
|
||||||
pub vendor: Vendor,
|
|
||||||
pub id: u16,
|
|
||||||
}
|
|
||||||
impl DeviceID {
|
|
||||||
pub const fn new(vendor: Vendor, id: u16) -> Self {
|
|
||||||
Self { vendor, id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const VMWARE_SVGA2: DeviceID = DeviceID::new(VMware, 0x0405);
|
|
||||||
pub const S3INC_TRIO64V2: DeviceID = DeviceID::new(S3Inc, 0x8900);
|
|
674
ableos/src/devices/pci/ide.rs
Normal file
674
ableos/src/devices/pci/ide.rs
Normal file
|
@ -0,0 +1,674 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use core::num::TryFromIntError;
|
||||||
|
|
||||||
|
// FIXME: platform agnostic-ify these
|
||||||
|
use x86_64::instructions::{interrupts, port::Port};
|
||||||
|
use x86_64::structures::paging::{mapper::MapToError, Mapper, Page, PhysFrame, Size4KiB};
|
||||||
|
use x86_64::structures::paging::{FrameAllocator, FrameDeallocator};
|
||||||
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
|
use crate::arch::memory::BootInfoFrameAllocator;
|
||||||
|
use crate::devices::pci::check_device;
|
||||||
|
|
||||||
|
use super::PciDeviceInfo;
|
||||||
|
|
||||||
|
// FIXME: un-hardcode these
|
||||||
|
const PRDT_START: u64 = 0xffff_ffff_0000_0000;
|
||||||
|
const BUFFER_START: u64 = 0xffff_ffff_0000_1000;
|
||||||
|
|
||||||
|
/// ATA logical sector size, in bytes
|
||||||
|
const SECTOR_SIZE: u16 = 512;
|
||||||
|
|
||||||
|
/// Bus Master IDE Command
|
||||||
|
const BMIC_OFFSET: u16 = 0;
|
||||||
|
/// Bus Master IDE Status
|
||||||
|
const BMIS_OFFSET: u16 = 2;
|
||||||
|
/// Bus Master IDE Descriptor Table Pointer
|
||||||
|
const BMIDTP_OFFSET: u16 = 4;
|
||||||
|
|
||||||
|
/// Bus Master IDE Secondary Offset
|
||||||
|
const BMI_SECONDARY: u16 = 8;
|
||||||
|
|
||||||
|
/// Primary command block offset
|
||||||
|
const PRIMARY_COMMAND: u16 = 0x01F0;
|
||||||
|
|
||||||
|
/// Secondary command block offset
|
||||||
|
const SECONDARY_COMMAND: u16 = 0x0170;
|
||||||
|
|
||||||
|
/// Data register offset
|
||||||
|
const DATA_OFFSET: u16 = 0;
|
||||||
|
|
||||||
|
/// Sector count register offset
|
||||||
|
const SECCOUNT_OFFSET: u16 = 2;
|
||||||
|
|
||||||
|
/// LBA0 register offset
|
||||||
|
const LBA0_OFFSET: u16 = 3;
|
||||||
|
|
||||||
|
/// LBA1 register offset
|
||||||
|
const LBA1_OFFSET: u16 = 4;
|
||||||
|
|
||||||
|
/// LBA2 register offset
|
||||||
|
const LBA2_OFFSET: u16 = 5;
|
||||||
|
|
||||||
|
/// Drive/Head register offset
|
||||||
|
const DRIVE_HEAD_OFFSET: u16 = 6;
|
||||||
|
|
||||||
|
/// Command/status register offset
|
||||||
|
const COMMAND_STATUS_OFFSET: u16 = 7;
|
||||||
|
|
||||||
|
/// Secondary control block offset
|
||||||
|
const SECONDARY_CONTROL: u16 = 0x0374;
|
||||||
|
|
||||||
|
/// Primary control block offset
|
||||||
|
const PRIMARY_CONTROL: u16 = 0x03F4;
|
||||||
|
|
||||||
|
/// Alternative status offset
|
||||||
|
const ALT_STATUS_OFFSET: u16 = 2;
|
||||||
|
|
||||||
|
/// ATA identification command
|
||||||
|
const CMD_IDENTIFY: u8 = 0xEC;
|
||||||
|
|
||||||
|
/// ATA read using LBA28 DMA command
|
||||||
|
const CMD_READ_DMA: u8 = 0xC8;
|
||||||
|
|
||||||
|
/// ATA write using LBA28 DMA command
|
||||||
|
const CMD_WRITE_DMA: u8 = 0xCA;
|
||||||
|
|
||||||
|
/// ATA read using LBA48 DMA command
|
||||||
|
const CMD_READ_DMA_EXT: u8 = 0x25;
|
||||||
|
|
||||||
|
/// ATA write using LBA48 DMA command
|
||||||
|
const CMD_WRITE_DMA_EXT: u8 = 0x35;
|
||||||
|
|
||||||
|
pub struct PciIde {
|
||||||
|
device_info: PciDeviceInfo,
|
||||||
|
ide_devices: Vec<IdeDevice>,
|
||||||
|
prdt_frame: Option<PhysFrame>,
|
||||||
|
buffer_frames: Option<Vec<PhysFrame>>,
|
||||||
|
bmiba: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PciIde {
|
||||||
|
// FIXME: make this return a Result
|
||||||
|
pub fn new(bus: u8, device: u8) -> Option<Self> {
|
||||||
|
let device_info = check_device(bus, device)?;
|
||||||
|
device_info.enable_bus_mastering();
|
||||||
|
let idetim = unsafe { device_info.read(0, 0x40) };
|
||||||
|
trace!("idetim: {idetim:b}");
|
||||||
|
// FIXME: enable the right bits in idetim (and sidetim) to use fast timings
|
||||||
|
|
||||||
|
let mut ide_devices = Vec::with_capacity(4);
|
||||||
|
for ch in 0..2 {
|
||||||
|
let channel = if ch == 0 {
|
||||||
|
Channel::Primary
|
||||||
|
} else {
|
||||||
|
Channel::Secondary
|
||||||
|
};
|
||||||
|
'drive: for dr in 0..2 {
|
||||||
|
let drive = if dr == 0 { Drive::Master } else { Drive::Slave };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
select_drive(drive, channel);
|
||||||
|
// FIXME: clear sector count and lba0, lba1, lba2 registers
|
||||||
|
let status = ata_send_command(CMD_IDENTIFY, channel);
|
||||||
|
if status == 0 {
|
||||||
|
continue; // If status = 0, no device
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let status = {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
SECONDARY_COMMAND
|
||||||
|
} else {
|
||||||
|
PRIMARY_COMMAND
|
||||||
|
} + COMMAND_STATUS_OFFSET;
|
||||||
|
Port::<u8>::new(addr).read()
|
||||||
|
};
|
||||||
|
|
||||||
|
if status & 1 == 1 {
|
||||||
|
// if error (bit 0), device is not ATA
|
||||||
|
// FIXME: ATAPI devices
|
||||||
|
continue 'drive;
|
||||||
|
}
|
||||||
|
if !((status >> 7) & 1 == 1) && (status >> 3) & 1 == 1 {
|
||||||
|
// BSY cleared, DRQ set, everything is right
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read identification space of device
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
SECONDARY_COMMAND
|
||||||
|
} else {
|
||||||
|
PRIMARY_COMMAND
|
||||||
|
} + DATA_OFFSET;
|
||||||
|
let mut buffer = [0_u8; 512];
|
||||||
|
read_dword_buffer(addr, buffer.as_mut_ptr() as *mut _, 128);
|
||||||
|
// for (i, byte) in buffer.iter().enumerate() {
|
||||||
|
// if byte.is_ascii() {
|
||||||
|
// trace!("byte {i}: {byte:b}, ascii: {}", *byte as char);
|
||||||
|
// } else {
|
||||||
|
// trace!("byte {i}: {byte:b}");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if buffer[99] & 1 != 1 {
|
||||||
|
// FIXME: PIO mode support
|
||||||
|
error!("IDE drive {channel:?}/{drive:?} does not support DMA");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: CHS support
|
||||||
|
let lba48 = (buffer[167] >> 2) & 1 == 1;
|
||||||
|
|
||||||
|
let size = buffer[200] as u64
|
||||||
|
| (buffer[201] as u64) << 8
|
||||||
|
| (buffer[202] as u64) << 16
|
||||||
|
| (buffer[203] as u64) << 24
|
||||||
|
| (buffer[204] as u64) << 32
|
||||||
|
| (buffer[205] as u64) << 40
|
||||||
|
| (buffer[206] as u64) << 48
|
||||||
|
| (buffer[207] as u64) << 54;
|
||||||
|
trace!("IDE drive {channel:?}/{drive:?} has {size} sectors");
|
||||||
|
|
||||||
|
ide_devices.push(IdeDevice {
|
||||||
|
channel,
|
||||||
|
drive,
|
||||||
|
size,
|
||||||
|
lba48_support: lba48,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bmiba = device_info.bar(4) & 0xFFFFFFFC;
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
device_info,
|
||||||
|
ide_devices,
|
||||||
|
prdt_frame: None,
|
||||||
|
buffer_frames: None,
|
||||||
|
bmiba: bmiba.try_into().ok()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_dma_frame(
|
||||||
|
&mut self,
|
||||||
|
mapper: &mut impl Mapper<Size4KiB>,
|
||||||
|
frame_allocator: &mut BootInfoFrameAllocator,
|
||||||
|
) -> Result<(), MapToError<Size4KiB>> {
|
||||||
|
use x86_64::structures::paging::PageTableFlags as Flags;
|
||||||
|
|
||||||
|
let prdt_frame = frame_allocator
|
||||||
|
.allocate_frame()
|
||||||
|
.ok_or(MapToError::FrameAllocationFailed)?;
|
||||||
|
|
||||||
|
let buffer_frames = {
|
||||||
|
let mut frame = frame_allocator
|
||||||
|
.allocate_frame()
|
||||||
|
.ok_or(MapToError::FrameAllocationFailed)?;
|
||||||
|
while !frame.start_address().is_aligned(0x10000u64) {
|
||||||
|
unsafe {
|
||||||
|
frame_allocator.deallocate_frame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = frame_allocator
|
||||||
|
.allocate_frame()
|
||||||
|
.ok_or(MapToError::FrameAllocationFailed)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut frames = Vec::with_capacity(16);
|
||||||
|
frames.push(frame);
|
||||||
|
for _ in 0..15 {
|
||||||
|
let frame = frame_allocator
|
||||||
|
.allocate_frame()
|
||||||
|
.ok_or(MapToError::FrameAllocationFailed)?;
|
||||||
|
frames.push(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
frames
|
||||||
|
};
|
||||||
|
let flags = Flags::NO_CACHE | Flags::PRESENT | Flags::WRITABLE;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
mapper
|
||||||
|
.map_to(
|
||||||
|
Page::containing_address(VirtAddr::new(PRDT_START)),
|
||||||
|
prdt_frame,
|
||||||
|
flags,
|
||||||
|
frame_allocator,
|
||||||
|
)?
|
||||||
|
.flush();
|
||||||
|
|
||||||
|
for (i, frame) in buffer_frames.iter().enumerate() {
|
||||||
|
mapper
|
||||||
|
.map_to(
|
||||||
|
Page::containing_address(VirtAddr::new(BUFFER_START + i as u64 * 0x1000)),
|
||||||
|
*frame,
|
||||||
|
flags,
|
||||||
|
frame_allocator,
|
||||||
|
)?
|
||||||
|
.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prdt_frame = Some(prdt_frame);
|
||||||
|
self.buffer_frames = Some(buffer_frames);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(
|
||||||
|
&mut self,
|
||||||
|
channel: Channel,
|
||||||
|
drive: Drive,
|
||||||
|
lba: u64,
|
||||||
|
sector_count: u16,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
) -> Result<(), TryFromIntError> {
|
||||||
|
let lba48_support = self
|
||||||
|
.ide_devices
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.channel == channel && d.drive == drive)
|
||||||
|
.map(|d| d.lba48_support)
|
||||||
|
.unwrap(); // FIXME: make this an error
|
||||||
|
let lba48 = lba > 0xFFFFFFF && lba48_support;
|
||||||
|
|
||||||
|
// FIXME: make this an error
|
||||||
|
assert!((lba48 && lba > 0xFFFFFFF) || (!lba48 && lba <= 0xFFFFFFF));
|
||||||
|
|
||||||
|
let byte_count = sector_count * SECTOR_SIZE;
|
||||||
|
|
||||||
|
// prepare PRD table
|
||||||
|
let prd = PRDT_START as *mut PhysRegionDescriptor;
|
||||||
|
unsafe {
|
||||||
|
(*prd).data_buffer = self.buffer_frames.as_ref().unwrap()[0]
|
||||||
|
.start_address()
|
||||||
|
.as_u64()
|
||||||
|
.try_into()?;
|
||||||
|
(*prd).byte_count = byte_count;
|
||||||
|
// this is the end of table
|
||||||
|
(*prd).eot = 1 << 7;
|
||||||
|
// this byte is reserved, we should probably set it to 0
|
||||||
|
(*prd)._0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.load_prdt(channel);
|
||||||
|
self.stop(channel);
|
||||||
|
self.set_read(channel);
|
||||||
|
self.clear_bmi_status(channel);
|
||||||
|
select_drive(drive, channel);
|
||||||
|
set_lba(channel, lba, sector_count, lba48);
|
||||||
|
|
||||||
|
if lba48 {
|
||||||
|
ata_send_command(CMD_READ_DMA_EXT, channel);
|
||||||
|
} else {
|
||||||
|
ata_send_command(CMD_READ_DMA, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.start(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let status = unsafe { self.bmi_status(channel) };
|
||||||
|
|
||||||
|
// FIXME: error handling
|
||||||
|
|
||||||
|
// Bit 2 (INT) set?
|
||||||
|
if (status >> 2) & 1 == 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Stop DMA
|
||||||
|
self.stop(channel);
|
||||||
|
|
||||||
|
// Clear the interrupt bit
|
||||||
|
self.clear_bmi_status(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..byte_count as u64 {
|
||||||
|
let addr = (BUFFER_START + i) as *mut u8;
|
||||||
|
buffer.push(unsafe { *addr });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(
|
||||||
|
&mut self,
|
||||||
|
channel: Channel,
|
||||||
|
drive: Drive,
|
||||||
|
lba: u64,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Result<(), TryFromIntError> {
|
||||||
|
// FIXME: make this an error
|
||||||
|
assert!(data.len() % SECTOR_SIZE as usize == 0);
|
||||||
|
|
||||||
|
let lba48_support = self
|
||||||
|
.ide_devices
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.channel == channel && d.drive == drive)
|
||||||
|
.map(|d| d.lba48_support)
|
||||||
|
.unwrap(); // FIXME: make this an error
|
||||||
|
let lba48 = lba > 0xFFFFFFF && lba48_support;
|
||||||
|
|
||||||
|
// FIXME: make this an error
|
||||||
|
assert!((lba48 && lba > 0xFFFFFFF) || (!lba48 && lba <= 0xFFFFFFF));
|
||||||
|
|
||||||
|
let byte_count = data.len() as u16;
|
||||||
|
let sector_count = byte_count / SECTOR_SIZE;
|
||||||
|
|
||||||
|
// prepare PRD table
|
||||||
|
let prd = PRDT_START as *mut PhysRegionDescriptor;
|
||||||
|
unsafe {
|
||||||
|
(*prd).data_buffer = self.buffer_frames.as_ref().unwrap()[0]
|
||||||
|
.start_address()
|
||||||
|
.as_u64()
|
||||||
|
.try_into()?;
|
||||||
|
(*prd).byte_count = byte_count;
|
||||||
|
// this is the end of table
|
||||||
|
(*prd).eot = 1 << 7;
|
||||||
|
// this byte is reserved, we should probably set it to 0
|
||||||
|
(*prd)._0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the data over to the DMA buffer
|
||||||
|
for i in 0..byte_count {
|
||||||
|
let addr = (BUFFER_START + i as u64) as *mut u8;
|
||||||
|
unsafe {
|
||||||
|
*addr = *data.get(i as usize).unwrap_or(&0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.load_prdt(channel);
|
||||||
|
self.stop(channel);
|
||||||
|
self.set_write(channel);
|
||||||
|
self.clear_bmi_status(channel);
|
||||||
|
select_drive(drive, channel);
|
||||||
|
set_lba(channel, lba, sector_count, lba48);
|
||||||
|
|
||||||
|
if lba48 {
|
||||||
|
ata_send_command(CMD_WRITE_DMA_EXT, channel);
|
||||||
|
} else {
|
||||||
|
ata_send_command(CMD_WRITE_DMA, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.start(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let status = unsafe { self.bmi_status(channel) };
|
||||||
|
|
||||||
|
// FIXME: error handling
|
||||||
|
|
||||||
|
// Bit 2 (INT) set?
|
||||||
|
if (status >> 2) & 1 == 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Stop DMA
|
||||||
|
self.stop(channel);
|
||||||
|
|
||||||
|
// Clear the interrupt bit
|
||||||
|
self.clear_bmi_status(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_info(&self) -> PciDeviceInfo {
|
||||||
|
self.device_info
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn load_prdt(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIDTP_OFFSET;
|
||||||
|
Port::<u32>::new(addr).write(
|
||||||
|
self.prdt_frame
|
||||||
|
.unwrap()
|
||||||
|
.start_address()
|
||||||
|
.as_u64()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn start(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIC_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
let mut bmic = port.read();
|
||||||
|
// start transfer
|
||||||
|
bmic |= 1;
|
||||||
|
// write the new bmic
|
||||||
|
port.write(bmic);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn stop(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIC_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
let mut bmic = port.read();
|
||||||
|
// stop ongoing transfer
|
||||||
|
bmic &= !1;
|
||||||
|
// write the new bmic
|
||||||
|
port.write(bmic);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_read(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIC_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
let mut bmic = port.read();
|
||||||
|
// mark bit 3 as 0 (read)
|
||||||
|
bmic &= !(1 << 3);
|
||||||
|
// write the new bmic
|
||||||
|
port.write(bmic);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_write(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIC_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
let mut bmic = port.read();
|
||||||
|
// mark bit 3 as 1 (write)
|
||||||
|
bmic |= 1 << 3;
|
||||||
|
// write the new bmic
|
||||||
|
port.write(bmic);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn bmi_status(&self, channel: Channel) -> u8 {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIS_OFFSET;
|
||||||
|
let mut port = Port::new(addr);
|
||||||
|
port.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn clear_bmi_status(&self, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
BMI_SECONDARY
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} + self.bmiba
|
||||||
|
+ BMIS_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
let mut bmis = port.read();
|
||||||
|
// write 1 to bits 1 (DMA error) and 2 (int status) which clears them
|
||||||
|
bmis |= 1 << 1 | 1 << 2;
|
||||||
|
// write the new bmis
|
||||||
|
port.write(bmis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn select_drive(drive: Drive, channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
SECONDARY_COMMAND
|
||||||
|
} else {
|
||||||
|
PRIMARY_COMMAND
|
||||||
|
} + DRIVE_HEAD_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
// FIXME: CHS support
|
||||||
|
let drive_command = if drive.slave() {
|
||||||
|
// slave & LBA
|
||||||
|
0b11110000
|
||||||
|
} else {
|
||||||
|
// master & LBA
|
||||||
|
0b11100000
|
||||||
|
};
|
||||||
|
|
||||||
|
// write the new drive/head register
|
||||||
|
port.write(drive_command);
|
||||||
|
ata_delay(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send ATA command and read status afterwards
|
||||||
|
unsafe fn ata_send_command(command: u8, channel: Channel) -> u8 {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
SECONDARY_COMMAND
|
||||||
|
} else {
|
||||||
|
PRIMARY_COMMAND
|
||||||
|
} + COMMAND_STATUS_OFFSET;
|
||||||
|
let mut port = Port::new(addr);
|
||||||
|
port.write(command);
|
||||||
|
ata_delay(channel);
|
||||||
|
port.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the alternate status register 14 times to create a ~420ns delay
|
||||||
|
unsafe fn ata_delay(channel: Channel) {
|
||||||
|
let addr = if channel.secondary() {
|
||||||
|
SECONDARY_CONTROL
|
||||||
|
} else {
|
||||||
|
PRIMARY_CONTROL
|
||||||
|
} + ALT_STATUS_OFFSET;
|
||||||
|
let mut port: Port<u8> = Port::new(addr);
|
||||||
|
for _ in 0..14 {
|
||||||
|
port.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set LBA and sector count registers. sector_count of 0 means 65536 sectors
|
||||||
|
unsafe fn set_lba(channel: Channel, lba: u64, sector_count: u16, lba48: bool) {
|
||||||
|
let command_block = if channel.secondary() {
|
||||||
|
SECONDARY_COMMAND
|
||||||
|
} else {
|
||||||
|
PRIMARY_COMMAND
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut seccount = Port::new(command_block + SECCOUNT_OFFSET);
|
||||||
|
let mut lba0 = Port::new(command_block + LBA0_OFFSET);
|
||||||
|
let mut lba1 = Port::new(command_block + LBA1_OFFSET);
|
||||||
|
let mut lba2 = Port::new(command_block + LBA2_OFFSET);
|
||||||
|
let mut head = Port::new(command_block + DRIVE_HEAD_OFFSET);
|
||||||
|
let head_value: u8 = head.read();
|
||||||
|
|
||||||
|
let lba_bytes = lba.to_le_bytes();
|
||||||
|
let sector_count_bytes = sector_count.to_le_bytes();
|
||||||
|
|
||||||
|
// write the new LBA & sector count registers
|
||||||
|
// FIXME: CHS support
|
||||||
|
if lba48 {
|
||||||
|
seccount.write(sector_count_bytes[1]);
|
||||||
|
lba0.write(lba_bytes[3]);
|
||||||
|
lba1.write(lba_bytes[4]);
|
||||||
|
lba2.write(lba_bytes[5]);
|
||||||
|
} else {
|
||||||
|
head.write(head_value | (lba_bytes[3] & 0x0F));
|
||||||
|
}
|
||||||
|
seccount.write(sector_count_bytes[0]);
|
||||||
|
lba0.write(lba_bytes[0]);
|
||||||
|
lba1.write(lba_bytes[1]);
|
||||||
|
lba2.write(lba_bytes[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_dword_buffer(port: u16, buffer: *mut u32, mut count: u32) {
|
||||||
|
// FIXME: this assumes x86-64
|
||||||
|
interrupts::without_interrupts(|| {
|
||||||
|
asm!("
|
||||||
|
cld
|
||||||
|
repne
|
||||||
|
insd",
|
||||||
|
in("di") buffer,
|
||||||
|
in("dx") port,
|
||||||
|
inout("cx") count,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdeDevice {
|
||||||
|
pub channel: Channel,
|
||||||
|
pub drive: Drive,
|
||||||
|
pub size: u64, // in sectors
|
||||||
|
pub lba48_support: bool,
|
||||||
|
// FIXME: model
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Channel {
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
fn secondary(&self) -> bool {
|
||||||
|
matches!(self, Self::Secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Drive {
|
||||||
|
Master,
|
||||||
|
Slave,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drive {
|
||||||
|
fn slave(&self) -> bool {
|
||||||
|
matches!(self, Self::Slave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct PhysRegionDescriptor {
|
||||||
|
/// Pointer to the data buffer
|
||||||
|
pub data_buffer: u32,
|
||||||
|
/// Byte count, 64K maximum per PRD transfer. 0 means 64K
|
||||||
|
pub byte_count: u16,
|
||||||
|
/// Reserved byte
|
||||||
|
pub _0: u8,
|
||||||
|
/// MSB marks end of transfer
|
||||||
|
pub eot: u8,
|
||||||
|
}
|
|
@ -1,212 +1,71 @@
|
||||||
// The MIT License (MIT)
|
/*
|
||||||
// Copyright (c) 2021 trashbyte
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||||||
// See LICENSE.txt for full license
|
*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
extern crate alloc;
|
pub mod class;
|
||||||
use alloc::{format, string::String, vec::Vec};
|
pub mod device;
|
||||||
use core::fmt::{Display, Error, Formatter};
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde;
|
|
||||||
|
|
||||||
pub mod devices;
|
|
||||||
mod enums;
|
|
||||||
pub mod support;
|
|
||||||
pub mod vendors;
|
pub mod vendors;
|
||||||
pub use enums::*;
|
|
||||||
|
|
||||||
pub mod port;
|
// MassStorage_IDE (0x0101)
|
||||||
use port::*;
|
pub mod ide;
|
||||||
|
|
||||||
use self::devices::DeviceID;
|
use alloc::sync::Arc;
|
||||||
|
pub use class::*;
|
||||||
|
pub use device::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use spin::Mutex;
|
||||||
|
use x86_64::structures::paging::{Mapper, Size4KiB};
|
||||||
|
|
||||||
// PciDeviceInfo ///////////////////////////////////////////////////////////////
|
use crate::arch::memory::BootInfoFrameAllocator;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
// MassStorage_IDE (0x0101)
|
||||||
#[derive(Clone, Debug)]
|
use self::ide::PciIde;
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
/// A struct containing info about a PCI device.
|
lazy_static! {
|
||||||
pub struct PciDeviceInfo {
|
pub static ref PCI_DEVICES: Mutex<Vec<Arc<Mutex<PciDevice>>>> = Default::default();
|
||||||
pub device: u8,
|
|
||||||
pub bus: u8,
|
|
||||||
pub device_id: DeviceID,
|
|
||||||
pub full_class: PciFullClass,
|
|
||||||
pub header_type: u8,
|
|
||||||
pub bars: [u32; 6],
|
|
||||||
pub supported_fns: [bool; 8],
|
|
||||||
pub interrupt_line: u8,
|
|
||||||
pub interrupt_pin: u8,
|
|
||||||
}
|
|
||||||
impl PciDeviceInfo {
|
|
||||||
/// Get the class of the PCI device as a PciClass
|
|
||||||
pub fn class(&self) -> PciClass {
|
|
||||||
PciClass::from_u8(((self.full_class.as_u16() >> 8) & 0xFF) as u8)
|
|
||||||
}
|
|
||||||
/// Get the full class of the PCI device as a PciFullClass
|
|
||||||
pub fn subclass(&self) -> PciClass {
|
|
||||||
PciClass::from_u8((self.full_class.as_u16() & 0xFF) as u8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for PciDeviceInfo {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
|
||||||
let vendor_name = &self.device_id.vendor;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"Device {} | Bus {:X} | Vendor: {}",
|
|
||||||
self.device, self.bus, vendor_name
|
|
||||||
)?;
|
|
||||||
writeln!(f, "{}", self.full_class)?;
|
|
||||||
writeln!(f, " Header type: {:X}", self.header_type)?;
|
|
||||||
write!(f, " Supported functions: 0")?;
|
|
||||||
for (i, b) in self.supported_fns.iter().enumerate().skip(1) {
|
|
||||||
if *b {
|
|
||||||
write!(f, ", {}", i)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(f)?;
|
|
||||||
write!(f, " BARs: [ ")?;
|
|
||||||
for i in self.bars.iter() {
|
|
||||||
if *i == 0 {
|
|
||||||
write!(f, "0x0 ")?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{:#010X} ", i)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(f, "]")?;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
" Interrupt line / pin: {} / {}",
|
|
||||||
self.interrupt_line, self.interrupt_pin
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public functions ////////////////////////////////////////////////////////////
|
#[non_exhaustive]
|
||||||
|
pub enum PciDevice {
|
||||||
/// Brute force scans for devices 0-31 on buses 0-255.
|
Ide(PciIde),
|
||||||
pub fn brute_force_scan() -> Vec<PciDeviceInfo> {
|
// Variant so that we aren't about irrefutable if-let patterns
|
||||||
let mut infos = Vec::new();
|
// FIXME: remove as soon as we have other variants
|
||||||
for bus in 0u8..=255 {
|
_0,
|
||||||
for device in 0u8..32 {
|
|
||||||
if let Some(info) = check_device(bus, device) {
|
|
||||||
infos.push(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
infos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal functions //////////////////////////////////////////////////////////
|
/// Enumerate PCI devices and run initialisation routines on ones we support
|
||||||
|
pub fn init(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut BootInfoFrameAllocator) {
|
||||||
|
for bus in 0..=255 {
|
||||||
|
for device in 0..32 {
|
||||||
|
if let Some(device_info) = device::check_device(bus, device) {
|
||||||
|
trace!("{device_info}");
|
||||||
|
match device_info.device_id {
|
||||||
|
// FIXME: Unknown class
|
||||||
|
S3_TRIO64V2 => {}
|
||||||
|
|
||||||
fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
|
// MassStorage_IDE (0x0101)
|
||||||
assert!(device < 32);
|
ide_controller if device_info.full_class == PciFullClass::MassStorage_IDE => {
|
||||||
let function = 0u8;
|
if !matches!(ide_controller, INTEL_PIIX3_IDE | INTEL_PIIX4_IDE) {
|
||||||
|
// not one of our tested IDE controllers, but
|
||||||
|
// we shouldn't have any problems
|
||||||
|
warn!("Unsupported PCI IDE controller device {device} on bus {bus}")
|
||||||
|
}
|
||||||
|
|
||||||
let (device_id, vendor_id) = get_ids(bus, device, function);
|
let mut ide = PciIde::new(bus, device).unwrap();
|
||||||
if vendor_id == 0xFFFF {
|
ide.allocate_dma_frame(mapper, frame_allocator).unwrap();
|
||||||
// Device doesn't exist
|
let mut devices = PCI_DEVICES.lock();
|
||||||
return None;
|
devices.push(Arc::new(Mutex::new(PciDevice::Ide(ide))));
|
||||||
}
|
}
|
||||||
|
|
||||||
let class = unsafe { pci_config_read(bus, device, 0, 0x8) };
|
// Display_VGA (0x0300)
|
||||||
let class = (class >> 16) & 0x0000FFFF;
|
VMWARE_SVGA2 => {}
|
||||||
let pci_class = PciFullClass::from_u16(class as u16);
|
_ => {
|
||||||
let header_type = get_header_type(bus, device, function);
|
trace!("Unknown PCI device {device} on bus {bus}")
|
||||||
|
}
|
||||||
let mut supported_fns = [true, false, false, false, false, false, false, false];
|
|
||||||
if (header_type & 0x80) != 0 {
|
|
||||||
// It is a multi-function device, so check remaining functions
|
|
||||||
for function in 0u8..8 {
|
|
||||||
if get_ids(bus, device, function).1 != 0xFFFF {
|
|
||||||
if check_function(bus, device, function) {
|
|
||||||
supported_fns[function as usize] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bars = [0, 0, 0, 0, 0, 0];
|
|
||||||
unsafe {
|
|
||||||
bars[0] = pci_config_read(bus, device, 0, 0x10);
|
|
||||||
bars[1] = pci_config_read(bus, device, 0, 0x14);
|
|
||||||
bars[2] = pci_config_read(bus, device, 0, 0x18);
|
|
||||||
bars[3] = pci_config_read(bus, device, 0, 0x1C);
|
|
||||||
bars[4] = pci_config_read(bus, device, 0, 0x20);
|
|
||||||
bars[5] = pci_config_read(bus, device, 0, 0x24);
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_row = unsafe { pci_config_read(bus, device, 0, 0x3C) };
|
|
||||||
|
|
||||||
Some(PciDeviceInfo {
|
|
||||||
device,
|
|
||||||
bus,
|
|
||||||
device_id: DeviceID {
|
|
||||||
vendor: vendor_id.into(),
|
|
||||||
id: device_id,
|
|
||||||
},
|
|
||||||
// vendor_id,
|
|
||||||
full_class: pci_class,
|
|
||||||
header_type,
|
|
||||||
bars,
|
|
||||||
supported_fns,
|
|
||||||
interrupt_line: (last_row & 0xFF) as u8,
|
|
||||||
interrupt_pin: ((last_row >> 8) & 0xFF) as u8,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
|
|
||||||
let bus = bus as u32;
|
|
||||||
let device = device as u32;
|
|
||||||
let func = func as u32;
|
|
||||||
let offset = offset as u32;
|
|
||||||
// construct address param
|
|
||||||
let address =
|
|
||||||
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
|
|
||||||
|
|
||||||
// write address
|
|
||||||
Port::<u32>::new(0xCF8).write(address);
|
|
||||||
|
|
||||||
// read data
|
|
||||||
Port::<u32>::new(0xCFC).read()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
|
|
||||||
let bus = bus as u32;
|
|
||||||
let device = device as u32;
|
|
||||||
let func = func as u32;
|
|
||||||
let offset = offset as u32;
|
|
||||||
// construct address param
|
|
||||||
let address =
|
|
||||||
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
|
|
||||||
|
|
||||||
// write address
|
|
||||||
Port::<u32>::new(0xCF8).write(address);
|
|
||||||
|
|
||||||
// write data
|
|
||||||
Port::<u32>::new(0xCFC).write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
|
|
||||||
assert!(device < 32);
|
|
||||||
assert!(function < 8);
|
|
||||||
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
|
|
||||||
((res >> 16) & 0xFF) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_function(bus: u8, device: u8, function: u8) -> bool {
|
|
||||||
assert!(device < 32);
|
|
||||||
assert!(function < 8);
|
|
||||||
get_ids(bus, device, function).1 != 0xFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
|
|
||||||
assert!(device < 32);
|
|
||||||
assert!(function < 8);
|
|
||||||
let res = unsafe { pci_config_read(bus, device, function, 0) };
|
|
||||||
let dev_id = ((res >> 16) & 0xFFFF) as u16;
|
|
||||||
let vnd_id = (res & 0xFFFF) as u16;
|
|
||||||
(dev_id, vnd_id)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
/// Trait for limiting [Port] to only being able to read/write u8/16/32.
|
|
||||||
pub(crate) trait PortRW {
|
|
||||||
/// Read a value (self) from the port
|
|
||||||
unsafe fn read_port(port: u16) -> Self;
|
|
||||||
/// Write a value (self) to the port
|
|
||||||
unsafe fn write_port(port: u16, value: Self);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PortRW Implementations //////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
impl PortRW for u8 {
|
|
||||||
unsafe fn read_port(port: u16) -> Self {
|
|
||||||
let value: u8;
|
|
||||||
asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags));
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn write_port(port: u16, value: Self) {
|
|
||||||
asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PortRW for u16 {
|
|
||||||
unsafe fn read_port(port: u16) -> Self {
|
|
||||||
let value: u16;
|
|
||||||
asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags));
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn write_port(port: u16, value: Self) {
|
|
||||||
asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PortRW for u32 {
|
|
||||||
unsafe fn read_port(port: u16) -> Self {
|
|
||||||
let value: u32;
|
|
||||||
asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags));
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn write_port(port: u16, value: Self) {
|
|
||||||
asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple wrapper around the asm instructions needed to read/write I/O ports.
|
|
||||||
pub(crate) struct Port<T: PortRW> {
|
|
||||||
addr: u16,
|
|
||||||
_phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
impl<T: PortRW> Port<T> {
|
|
||||||
/// Create a new `Port` with the given address and data size
|
|
||||||
pub(crate) fn new(addr: u16) -> Self {
|
|
||||||
Self {
|
|
||||||
addr,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a value from the port
|
|
||||||
pub(crate) unsafe fn read(&self) -> T {
|
|
||||||
T::read_port(self.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a value to the port
|
|
||||||
pub(crate) unsafe fn write(&self, value: T) {
|
|
||||||
T::write_port(self.addr, value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
use super::devices::*;
|
|
||||||
|
|
||||||
pub fn check_pci_support(device_id: DeviceID) -> bool {
|
|
||||||
match device_id {
|
|
||||||
VMWARE_SVGA2 => true,
|
|
||||||
S3INC_TRIO64V2 => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,57 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone, Eq)]
|
#[derive(PartialEq, Debug, Copy, Clone, Eq)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum Vendor {
|
pub enum Vendor {
|
||||||
|
ThreeDfxInteractiveInc = 0x121a,
|
||||||
|
ThreeDLabs = 0x3d3d,
|
||||||
|
AllianceSemiconductorCorp = 0x1142,
|
||||||
|
ARKLogicInc = 0xedd8,
|
||||||
|
ATITechnologiesInc = 0x1002,
|
||||||
|
AvanceLogicIncALI = 0x1005,
|
||||||
|
ChipsandTechnologies = 0x102c,
|
||||||
|
CirrusLogic = 0x1013,
|
||||||
|
Compaq = 0x0e11,
|
||||||
|
CyrixCorp = 0x1078,
|
||||||
|
DiamondMultimediaSystems = 0x1092,
|
||||||
|
DigitalEquipmentCorp = 0x1011,
|
||||||
|
Iit = 0x1061,
|
||||||
|
IntegratedMicroSolutionsInc = 0x10e0,
|
||||||
|
IntelCorp = 0x8086,
|
||||||
|
IntergraphicsSystems = 0x10ea,
|
||||||
|
MacronixInc = 0x10d9,
|
||||||
|
MatroxGraphicsInc = 0x102b,
|
||||||
|
MiroComputersProductsAG = 0x1031,
|
||||||
|
NationalSemiconductorCorp = 0x100b,
|
||||||
|
NeoMagicCorp = 0x10c8,
|
||||||
|
Number9ComputerCompany = 0x105d,
|
||||||
|
NVidiaCorporation = 0x10de,
|
||||||
|
NVidiaSgsthomson = 0x12d2,
|
||||||
|
OakTechnologyInc = 0x104e,
|
||||||
Qemu = 0x1234,
|
Qemu = 0x1234,
|
||||||
VMware = 0x15AD,
|
QuantumDesignsHKLtd = 0x1098,
|
||||||
VirtIO = 0x1AF4,
|
Real3D = 0x003d,
|
||||||
Ati = 1002,
|
Rendition = 0x1163,
|
||||||
Intel = 0x8086,
|
|
||||||
S3Inc = 0x5333,
|
S3Inc = 0x5333,
|
||||||
|
SierraSemiconductor = 0x10a8,
|
||||||
|
SiliconIntegratedSystemsSiS = 0x1039,
|
||||||
|
SiliconMotionInc = 0x126f,
|
||||||
|
STBSystemsInc = 0x10b4,
|
||||||
|
TexasInstruments = 0x104c,
|
||||||
|
ToshibaAmericaInfoSystems = 0x1179,
|
||||||
|
TridentMicrosystems = 0x1023,
|
||||||
|
TsengLabsInc = 0x100c,
|
||||||
|
TundraSemiconductorCorp = 0x10e3,
|
||||||
|
VIATechnologiesInc = 0x1106,
|
||||||
|
VirtIO = 0x1AF4,
|
||||||
|
VMWareInc = 0x15ad,
|
||||||
|
Weitek = 0x100e,
|
||||||
Unknown(u16),
|
Unknown(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +59,49 @@ impl From<u16> for Vendor {
|
||||||
fn from(vendor_id: u16) -> Self {
|
fn from(vendor_id: u16) -> Self {
|
||||||
use Vendor::*;
|
use Vendor::*;
|
||||||
match vendor_id {
|
match vendor_id {
|
||||||
0x15AD => VMware,
|
0x121a => ThreeDfxInteractiveInc,
|
||||||
0x8086 => Intel,
|
0x3d3d => ThreeDLabs,
|
||||||
1002 => Ati,
|
0x1142 => AllianceSemiconductorCorp,
|
||||||
|
0xedd8 => ARKLogicInc,
|
||||||
|
0x1002 => ATITechnologiesInc,
|
||||||
|
0x1005 => AvanceLogicIncALI,
|
||||||
|
0x102c => ChipsandTechnologies,
|
||||||
|
0x1013 => CirrusLogic,
|
||||||
|
0x0e11 => Compaq,
|
||||||
|
0x1078 => CyrixCorp,
|
||||||
|
0x1092 => DiamondMultimediaSystems,
|
||||||
|
0x1011 => DigitalEquipmentCorp,
|
||||||
|
0x1061 => Iit,
|
||||||
|
0x10e0 => IntegratedMicroSolutionsInc,
|
||||||
|
0x8086 => IntelCorp,
|
||||||
|
0x10ea => IntergraphicsSystems,
|
||||||
|
0x10d9 => MacronixInc,
|
||||||
|
0x102b => MatroxGraphicsInc,
|
||||||
|
0x1031 => MiroComputersProductsAG,
|
||||||
|
0x100b => NationalSemiconductorCorp,
|
||||||
|
0x10c8 => NeoMagicCorp,
|
||||||
|
0x105d => Number9ComputerCompany,
|
||||||
|
0x10de => NVidiaCorporation,
|
||||||
|
0x12d2 => NVidiaSgsthomson,
|
||||||
|
0x104e => OakTechnologyInc,
|
||||||
|
0x1234 => Qemu,
|
||||||
|
0x1098 => QuantumDesignsHKLtd,
|
||||||
|
0x003d => Real3D,
|
||||||
|
0x1163 => Rendition,
|
||||||
|
0x5333 => S3Inc,
|
||||||
|
0x10a8 => SierraSemiconductor,
|
||||||
|
0x1039 => SiliconIntegratedSystemsSiS,
|
||||||
|
0x126f => SiliconMotionInc,
|
||||||
|
0x10b4 => STBSystemsInc,
|
||||||
|
0x104c => TexasInstruments,
|
||||||
|
0x1179 => ToshibaAmericaInfoSystems,
|
||||||
|
0x1023 => TridentMicrosystems,
|
||||||
|
0x100c => TsengLabsInc,
|
||||||
|
0x10e3 => TundraSemiconductorCorp,
|
||||||
|
0x1106 => VIATechnologiesInc,
|
||||||
|
0x1AF4 => VirtIO,
|
||||||
|
0x15ad => VMWareInc,
|
||||||
|
0x100e => Weitek,
|
||||||
id => Unknown(id),
|
id => Unknown(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,12 +111,49 @@ impl Into<u16> for Vendor {
|
||||||
fn into(self) -> u16 {
|
fn into(self) -> u16 {
|
||||||
use Vendor::*;
|
use Vendor::*;
|
||||||
match self {
|
match self {
|
||||||
VMware => 0x15AD,
|
ThreeDfxInteractiveInc => 0x121a,
|
||||||
Ati => 1002,
|
ThreeDLabs => 0x3d3d,
|
||||||
|
AllianceSemiconductorCorp => 0x1142,
|
||||||
|
ARKLogicInc => 0xedd8,
|
||||||
|
ATITechnologiesInc => 0x1002,
|
||||||
|
AvanceLogicIncALI => 0x1005,
|
||||||
|
ChipsandTechnologies => 0x102c,
|
||||||
|
CirrusLogic => 0x1013,
|
||||||
|
Compaq => 0x0e11,
|
||||||
|
CyrixCorp => 0x1078,
|
||||||
|
DiamondMultimediaSystems => 0x1092,
|
||||||
|
DigitalEquipmentCorp => 0x1011,
|
||||||
|
Iit => 0x1061,
|
||||||
|
IntegratedMicroSolutionsInc => 0x10e0,
|
||||||
|
IntelCorp => 0x8086,
|
||||||
|
IntergraphicsSystems => 0x10ea,
|
||||||
|
MacronixInc => 0x10d9,
|
||||||
|
MatroxGraphicsInc => 0x102b,
|
||||||
|
MiroComputersProductsAG => 0x1031,
|
||||||
|
NationalSemiconductorCorp => 0x100b,
|
||||||
|
NeoMagicCorp => 0x10c8,
|
||||||
|
Number9ComputerCompany => 0x105d,
|
||||||
|
NVidiaCorporation => 0x10de,
|
||||||
|
NVidiaSgsthomson => 0x12d2,
|
||||||
|
OakTechnologyInc => 0x104e,
|
||||||
Qemu => 0x1234,
|
Qemu => 0x1234,
|
||||||
VirtIO => 0x1AF4,
|
QuantumDesignsHKLtd => 0x1098,
|
||||||
Intel => 0x8086,
|
Real3D => 0x003d,
|
||||||
|
Rendition => 0x1163,
|
||||||
S3Inc => 0x5333,
|
S3Inc => 0x5333,
|
||||||
|
SierraSemiconductor => 0x10a8,
|
||||||
|
SiliconIntegratedSystemsSiS => 0x1039,
|
||||||
|
SiliconMotionInc => 0x126f,
|
||||||
|
STBSystemsInc => 0x10b4,
|
||||||
|
TexasInstruments => 0x104c,
|
||||||
|
ToshibaAmericaInfoSystems => 0x1179,
|
||||||
|
TridentMicrosystems => 0x1023,
|
||||||
|
TsengLabsInc => 0x100c,
|
||||||
|
TundraSemiconductorCorp => 0x10e3,
|
||||||
|
VIATechnologiesInc => 0x1106,
|
||||||
|
VirtIO => 0x1AF4,
|
||||||
|
VMWareInc => 0x15ad,
|
||||||
|
Weitek => 0x100e,
|
||||||
Unknown(id) => id,
|
Unknown(id) => id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,20 +163,18 @@ impl Display for Vendor {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
use Vendor::*;
|
use Vendor::*;
|
||||||
|
|
||||||
let mut ret: String = match self {
|
match self {
|
||||||
Qemu => "\0PINK\0QEMU (0x1234)".into(),
|
Qemu => write!(f, "{}", "\0PINK\0QEMU (0x1234)"),
|
||||||
VirtIO => "\0PINK\0VirtIO (0x1AF4)".into(),
|
VirtIO => write!(f, "{}", "\0PINK\0VirtIO (0x1AF4)"),
|
||||||
VMware => "\0PINK\0VMWARE (0x15AD)".into(),
|
VMWareInc => write!(f, "{}", "\0PINK\0VMWARE (0x15AD)"),
|
||||||
S3Inc => "\0YELLOW\0S3 Incorporated (0x5333)".into(),
|
S3Inc => write!(f, "{}", "\0YELLOW\0S3 Incorporated (0x5333)"),
|
||||||
Intel => "\0BLUE\0Intel Corp. (0x8086)".into(),
|
IntelCorp => write!(f, "{}", "\0BLUE\0Intel Corp. (0x8086)"),
|
||||||
Ati => "\0RED\0ATI (0x1002)".into(),
|
ATITechnologiesInc => write!(f, "{}", "\0RED\0ATI (0x1002)"),
|
||||||
|
Unknown(id) => write!(f, "\0RED\0Unknown ({:#6})", id),
|
||||||
|
other => write!(f, "{other:?}"),
|
||||||
|
}?;
|
||||||
|
|
||||||
Unknown(id) => format!("\0RED\0Unknown ({:#6})", id),
|
write!(f, "\0RESET\0")?;
|
||||||
};
|
|
||||||
|
|
||||||
ret.push_str("\0RESET\0");
|
|
||||||
|
|
||||||
write!(f, "{}", ret)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
//! map the DeviceClass via <https://pci-ids.ucw.cz/read/PD>
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub enum Vendors {
|
|
||||||
ThreeDfxInteractiveInc = 0x121a,
|
|
||||||
ThreeDLabs = 0x3d3d,
|
|
||||||
AllianceSemiconductorCorp = 0x1142,
|
|
||||||
ARKLogicInc = 0xedd8,
|
|
||||||
ATITechnologiesInc = 0x1002,
|
|
||||||
AvanceLogicIncALI = 0x1005,
|
|
||||||
ChipsandTechnologies = 0x102c,
|
|
||||||
CirrusLogic = 0x1013,
|
|
||||||
Compaq = 0x0e11,
|
|
||||||
CyrixCorp = 0x1078,
|
|
||||||
DiamondMultimediaSystems = 0x1092,
|
|
||||||
DigitalEquipmentCorp = 0x1011,
|
|
||||||
Iit = 0x1061,
|
|
||||||
IntegratedMicroSolutionsInc = 0x10e0,
|
|
||||||
IntelCorp = 0x8086,
|
|
||||||
IntergraphicsSystems = 0x10ea,
|
|
||||||
MacronixInc = 0x10d9,
|
|
||||||
MatroxGraphicsInc = 0x102b,
|
|
||||||
MiroComputersProductsAG = 0x1031,
|
|
||||||
NationalSemiconductorCorp = 0x100b,
|
|
||||||
NeoMagicCorp = 0x10c8,
|
|
||||||
Number9ComputerCompany = 0x105d,
|
|
||||||
NVidiaCorporation = 0x10de,
|
|
||||||
NVidiaSgsthomson = 0x12d2,
|
|
||||||
OakTechnologyInc = 0x104e,
|
|
||||||
QuantumDesignsHKLtd = 0x1098,
|
|
||||||
Real3D = 0x003d,
|
|
||||||
Rendition = 0x1163,
|
|
||||||
S3Inc = 0x5333,
|
|
||||||
SierraSemiconductor = 0x10a8,
|
|
||||||
SiliconIntegratedSystemsSiS = 0x1039,
|
|
||||||
SiliconMotionInc = 0x126f,
|
|
||||||
STBSystemsInc = 0x10b4,
|
|
||||||
TexasInstruments = 0x104c,
|
|
||||||
ToshibaAmericaInfoSystems = 0x1179,
|
|
||||||
TridentMicrosystems = 0x1023,
|
|
||||||
TsengLabsInc = 0x100c,
|
|
||||||
TundraSemiconductorCorp = 0x10e3,
|
|
||||||
VIATechnologiesInc = 0x1106,
|
|
||||||
VMWareInc = 0x15ad,
|
|
||||||
Weitek = 0x100e,
|
|
||||||
Unknown = 0xffff,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PciDevice {
|
|
||||||
pub vendor: Vendors,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn scan_pci_bus() {}
|
|
|
@ -1,4 +1,4 @@
|
||||||
use kernel::device_interface::character::CharacterDevice;
|
use kernel::device_interface::CharacterDevice;
|
||||||
|
|
||||||
pub struct Serial {
|
pub struct Serial {
|
||||||
pub base: usize,
|
pub base: usize,
|
||||||
|
|
|
@ -8,21 +8,26 @@
|
||||||
|
|
||||||
use core::sync::atomic::AtomicU64;
|
use core::sync::atomic::AtomicU64;
|
||||||
|
|
||||||
|
use crate::arch::memory::BootInfoFrameAllocator;
|
||||||
use crate::arch::{drivers::sysinfo::master, init, sloop};
|
use crate::arch::{drivers::sysinfo::master, init, sloop};
|
||||||
|
use crate::devices::pci;
|
||||||
use crate::relib::network::socket::{SimpleSock, Socket};
|
use crate::relib::network::socket::{SimpleSock, Socket};
|
||||||
use crate::{
|
use crate::{boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM};
|
||||||
boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM,
|
|
||||||
};
|
|
||||||
use crate::{filesystem, hardware};
|
use crate::{filesystem, hardware};
|
||||||
use kernel::KERNEL_VERSION;
|
use kernel::KERNEL_VERSION;
|
||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
|
|
||||||
|
// FIXME: platform agnostic paging stuff
|
||||||
|
use x86_64::structures::paging::{Mapper, Size4KiB};
|
||||||
|
|
||||||
// TODO: Change this structure to allow for multiple cores loaded
|
// TODO: Change this structure to allow for multiple cores loaded
|
||||||
pub static KERNEL_CONF: Lazy<KernelConfig> = Lazy::new(KernelConfig::new);
|
pub static KERNEL_CONF: Lazy<KernelConfig> = Lazy::new(KernelConfig::new);
|
||||||
|
|
||||||
/// The main entry point of the kernel
|
/// The main entry point of the kernel
|
||||||
#[no_mangle]
|
pub fn kernel_main(
|
||||||
pub fn kernel_main() -> ! {
|
mut mapper: impl Mapper<Size4KiB>,
|
||||||
|
mut frame_allocator: BootInfoFrameAllocator,
|
||||||
|
) -> ! {
|
||||||
init::init();
|
init::init();
|
||||||
|
|
||||||
// /*
|
// /*
|
||||||
|
@ -39,6 +44,8 @@ pub fn kernel_main() -> ! {
|
||||||
// term.draw_term();
|
// term.draw_term();
|
||||||
// drop(term);
|
// drop(term);
|
||||||
|
|
||||||
|
pci::init(&mut mapper, &mut frame_allocator);
|
||||||
|
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
hardware::init_mouse();
|
hardware::init_mouse();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
|
@ -6,17 +6,14 @@
|
||||||
|
|
||||||
use crate::arch::drivers::sysinfo::master;
|
use crate::arch::drivers::sysinfo::master;
|
||||||
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
|
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
|
||||||
use crate::devices::pci::brute_force_scan;
|
use crate::devices::pci::ide::{Channel, Drive};
|
||||||
|
use crate::devices::pci::{PciDevice, PCI_DEVICES};
|
||||||
use crate::filesystem;
|
use crate::filesystem;
|
||||||
use crate::filesystem::vfs::VFS;
|
use crate::filesystem::vfs::VFS;
|
||||||
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
|
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
|
||||||
use crate::time::fetch_time;
|
use crate::time::fetch_time;
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::shutdown,
|
arch::shutdown, rhai_shell::KEYBUFF, vterm::VTerm, wasm_jumploader::run_program, KERNEL_STATE,
|
||||||
rhai_shell::KEYBUFF,
|
|
||||||
vterm::VTerm,
|
|
||||||
KERNEL_STATE,
|
|
||||||
wasm_jumploader::run_program,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use acpi::{AcpiTables, PlatformInfo};
|
use acpi::{AcpiTables, PlatformInfo};
|
||||||
|
@ -77,9 +74,6 @@ pub fn scratchpad() {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// bruh();
|
// bruh();
|
||||||
for x in brute_force_scan() {
|
|
||||||
println!("{}", x);
|
|
||||||
}
|
|
||||||
|
|
||||||
disable();
|
disable();
|
||||||
let tick_time = fetch_time();
|
let tick_time = fetch_time();
|
||||||
|
@ -120,6 +114,30 @@ pub fn scratchpad() {
|
||||||
BANNER_WIDTH
|
BANNER_WIDTH
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let pci_ide_device = {
|
||||||
|
let pci_devices = PCI_DEVICES.lock();
|
||||||
|
pci_devices
|
||||||
|
.iter()
|
||||||
|
.find_map(|device_ref| {
|
||||||
|
let device = device_ref.lock();
|
||||||
|
if let PciDevice::Ide(_) = &*device {
|
||||||
|
Some(device_ref.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut pci_ide_device = pci_ide_device.lock();
|
||||||
|
if let PciDevice::Ide(device) = &mut *pci_ide_device {
|
||||||
|
let mut first_sector = Vec::with_capacity(512);
|
||||||
|
device.read(Channel::Primary, Drive::Master, 0, 1, &mut first_sector).unwrap();
|
||||||
|
trace!("IDE Primary/Master sector 0: {first_sector:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
real_shell();
|
real_shell();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +254,7 @@ pub fn command_parser(user: String, command: String) {
|
||||||
"echo" => match conf_args.1.arguments.get("p") {
|
"echo" => match conf_args.1.arguments.get("p") {
|
||||||
Some(path) => echo_file(path.to_string()),
|
Some(path) => echo_file(path.to_string()),
|
||||||
None => println!("No path provided"),
|
None => println!("No path provided"),
|
||||||
}
|
},
|
||||||
"test" => {}
|
"test" => {}
|
||||||
"quit" => shutdown(),
|
"quit" => shutdown(),
|
||||||
"tree" => filesystem::tree("/").unwrap(),
|
"tree" => filesystem::tree("/").unwrap(),
|
||||||
|
@ -308,9 +326,7 @@ pub fn echo_file(path: String) {
|
||||||
let mut current_path = String::from("/");
|
let mut current_path = String::from("/");
|
||||||
current_path.push_str(&path);
|
current_path.push_str(&path);
|
||||||
let mut vfs = VFS.lock();
|
let mut vfs = VFS.lock();
|
||||||
let handle = vfs
|
let handle = vfs.resolve(¤t_path).unwrap();
|
||||||
.resolve(¤t_path)
|
|
||||||
.unwrap();
|
|
||||||
let file = vfs.fs_node(handle).unwrap();
|
let file = vfs.fs_node(handle).unwrap();
|
||||||
|
|
||||||
if file.is_dir() {
|
if file.is_dir() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::devices::Device::{Character, Vterm},
|
crate::devices::Device::{Block, Character, Vterm},
|
||||||
core::fmt::{Arguments, Error, Write},
|
core::fmt::{Arguments, Error, Write},
|
||||||
kernel::device_interface::character::CharacterDevice,
|
kernel::device_interface::CharacterDevice,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -35,6 +35,7 @@ impl StdIo {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Block(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ impl StdIo {
|
||||||
vterm.read_char().map(|c| buf.push(c));
|
vterm.read_char().map(|c| buf.push(c));
|
||||||
println!("{}", buf);
|
println!("{}", buf);
|
||||||
}
|
}
|
||||||
|
Block(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
kmain::KERNEL_CONF,
|
kmain::KERNEL_CONF,
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn device_handler(device: PciDeviceInfo) {
|
||||||
// Network => todo!(),
|
// Network => todo!(),
|
||||||
Display => {
|
Display => {
|
||||||
trace!("Display found");
|
trace!("Display found");
|
||||||
println!("{}", device.interrupt_pin);
|
println!("{}", device.interrupt_pin());
|
||||||
}
|
}
|
||||||
// Multimedia => todo!(),
|
// Multimedia => todo!(),
|
||||||
// Memory => todo!(),
|
// Memory => todo!(),
|
||||||
|
|
6
kernel/src/device_interface/block.rs
Normal file
6
kernel/src/device_interface/block.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
//! Block device interface
|
||||||
|
|
||||||
|
/// Block device interface
|
||||||
|
pub trait BlockDevice {
|
||||||
|
// TODO
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
//!
|
|
|
@ -1,4 +1,7 @@
|
||||||
//! Platform Agnostic Device
|
//! Platform Agnostic Device
|
||||||
|
|
||||||
pub mod block;
|
mod block;
|
||||||
pub mod character;
|
mod character;
|
||||||
|
|
||||||
|
pub use block::BlockDevice;
|
||||||
|
pub use character::CharacterDevice;
|
||||||
|
|
|
@ -135,14 +135,14 @@ fn test_push_number() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bytes,
|
bytes,
|
||||||
vec![
|
vec![
|
||||||
0b0101_1010, // 90
|
0b0101_1010, // 90
|
||||||
0b1001_1010, // 154
|
0b1001_1010, // 154
|
||||||
0b1100_1011, // 203
|
0b1100_1011, // 203
|
||||||
0b0110_1101, // 109
|
0b0110_1101, // 109
|
||||||
0b01_1001_00, // 100
|
0b01_1001_00, // 100
|
||||||
0b0111_1001, // 121
|
0b0111_1001, // 121
|
||||||
0b0111_0001, // 113
|
0b0111_0001, // 113
|
||||||
0b1000_0000, // 128
|
0b1000_0000, // 128
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -357,10 +357,7 @@ mod numeric_tests {
|
||||||
fn test_iso_18004_2006_example_1() {
|
fn test_iso_18004_2006_example_1() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
|
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(bits.into_bytes(), vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b0110_0001, 0b1000_0000]);
|
||||||
bits.into_bytes(),
|
|
||||||
vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b0110_0001, 0b1000_0000]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -389,16 +386,7 @@ mod numeric_tests {
|
||||||
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bits.into_bytes(),
|
bits.into_bytes(),
|
||||||
vec![
|
vec![0b0010_0000, 0b00000110, 0b0010_1011, 0b0011_0101, 0b0011_0111, 0b0000_1010, 0b01110101, 0b0010_1000,]
|
||||||
0b0010_0000,
|
|
||||||
0b00000110,
|
|
||||||
0b0010_1011,
|
|
||||||
0b0011_0101,
|
|
||||||
0b0011_0111,
|
|
||||||
0b0000_1010,
|
|
||||||
0b01110101,
|
|
||||||
0b0010_1000,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,10 +453,7 @@ mod alphanumeric_tests {
|
||||||
fn test_iso_18004_2006_example() {
|
fn test_iso_18004_2006_example() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
|
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(bits.into_bytes(), vec![0b0010_0000, 0b0010_1001, 0b11001110, 0b11100111, 0b0010_0001, 0b0000_0000]);
|
||||||
bits.into_bytes(),
|
|
||||||
vec![0b0010_0000, 0b0010_1001, 0b11001110, 0b11100111, 0b0010_0001, 0b0000_0000]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! Find the optimal data mode sequence to encode a piece of data.
|
//! Find the optimal data mode sequence to encode a piece of data.
|
||||||
use crate::types::{Mode, Version};
|
use crate::types::{Mode, Version};
|
||||||
|
|
||||||
|
|
||||||
use core::slice::Iter;
|
use core::slice::Iter;
|
||||||
|
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
|
@ -155,7 +154,7 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod parse_tests {
|
mod parse_tests {
|
||||||
use alloc::vec::{Vec};
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use crate::optimize::{Parser, Segment};
|
use crate::optimize::{Parser, Segment};
|
||||||
use crate::types::Mode;
|
use crate::types::Mode;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||||||
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{fs, process::Command};
|
use std::{fs, process::Command};
|
||||||
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "x86_ata"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = "*"
|
|
||||||
[dependencies.bit_field]
|
|
||||||
version = "0.10.0"
|
|
||||||
|
|
||||||
[dependencies.lazy_static]
|
|
||||||
features = ["spin_no_std"]
|
|
||||||
version = "1.4.0"
|
|
||||||
|
|
||||||
[dependencies.serde]
|
|
||||||
default-features = false
|
|
||||||
version = "1.0.126"
|
|
||||||
|
|
||||||
[dependencies.spin]
|
|
||||||
version = "0.9.0"
|
|
||||||
|
|
||||||
[dependencies.x86_64]
|
|
||||||
version = "0.14.3"
|
|
|
@ -1,380 +0,0 @@
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
/// Implementation Courtesy of MOROS.
|
|
||||||
/// Currently Only Supports ATA-PIO, with 24-bit LBA Addressing.
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use alloc::string::String;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use bit_field::BitField;
|
|
||||||
use core::hint::spin_loop;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::trace;
|
|
||||||
use spin::Mutex;
|
|
||||||
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
|
|
||||||
|
|
||||||
pub type BlockIndex = u32;
|
|
||||||
|
|
||||||
pub const ATA_BLOCK_SIZE: usize = 512;
|
|
||||||
|
|
||||||
fn sleep_ticks(ticks: usize) {
|
|
||||||
for _ in 0..=ticks {
|
|
||||||
x86_64::instructions::hlt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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),
|
|
||||||
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),
|
|
||||||
control_register: PortWriteOnly::new(ctrl_base),
|
|
||||||
drive_blockess_register: PortReadOnly::new(ctrl_base + 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
self.control_register.write(4); // Set SRST bit
|
|
||||||
sleep_ticks(2);
|
|
||||||
self.control_register.write(0); // Then clear it
|
|
||||||
sleep_ticks(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait(&mut self) {
|
|
||||||
for _ in 0..4 {
|
|
||||||
// Wait about 4 x 100 ns
|
|
||||||
unsafe {
|
|
||||||
self.alternate_status_register.read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_command(&mut self, cmd: Command) {
|
|
||||||
unsafe {
|
|
||||||
self.command_register.write(cmd as u8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 busy_loop(&mut self) {
|
|
||||||
self.wait();
|
|
||||||
let start = 0;
|
|
||||||
while self.is_busy() {
|
|
||||||
if 0 - start > 1 {
|
|
||||||
// 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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.busy_loop();
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read A single, 512-byte long slice from a given block
|
|
||||||
/// panics if buf isn't EXACTLY 512 Bytes long;
|
|
||||||
/// Example:
|
|
||||||
/// ```rust
|
|
||||||
/// // Read A Single block from a disk
|
|
||||||
/// pub fn read_single() {
|
|
||||||
/// use x86_ata::{init, ATA_BLOCK_SIZE, read};
|
|
||||||
/// // 1. Initialise ATA Subsystem. (Perform Once, on boot)
|
|
||||||
/// init().expect("Failed To Start ATA...");
|
|
||||||
/// // 2. Create a temporary buffer of size 512.
|
|
||||||
/// let mut buffer: [u8;ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE];
|
|
||||||
/// // 3. Pass the buffer over to the Subsystem, to be filled.
|
|
||||||
/// read(0, 0, 0, &mut buffer);
|
|
||||||
/// }
|
|
||||||
|
|
||||||
pub fn read(&mut self, drive: u8, block: BlockIndex, buf: &mut [u8]) {
|
|
||||||
assert!(buf.len() == 512);
|
|
||||||
trace!("Reading Block 0x{:8X}", block);
|
|
||||||
// trace!("{:?}", self);
|
|
||||||
|
|
||||||
self.setup(drive, block);
|
|
||||||
self.write_command(Command::Read);
|
|
||||||
self.busy_loop();
|
|
||||||
for i in (0..256).step_by(2) {
|
|
||||||
let data = self.read_data();
|
|
||||||
|
|
||||||
//log!("Read[{:08X}][{:02X}]: 0x{:04X}\n", block, i, data);
|
|
||||||
buf[i] = data.get_bits(0..8) as u8;
|
|
||||||
buf[i + 1] = data.get_bits(8..16) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write A single, 512-byte long slice to a given block
|
|
||||||
/// panics if buf isn't EXACTLY 512 Bytes long;
|
|
||||||
/// Example:
|
|
||||||
/// ```rust
|
|
||||||
/// // Read A Single block from a disk
|
|
||||||
/// pub fn write_single() {
|
|
||||||
/// use x86_ata::{init, ATA_BLOCK_SIZE, write};
|
|
||||||
/// // 1. Initialise ATA Subsystem. (Perform Once, on boot)
|
|
||||||
/// init().expect("Failed To Start ATA...");
|
|
||||||
/// // 2. Create a temporary buffer of size 512.
|
|
||||||
/// let buffer: [u8;ATA_BLOCK_SIZE] = [0; ATA_BLOCK_SIZE];
|
|
||||||
/// // 3. Pass the buffer over to the Subsystem, to be filled.
|
|
||||||
/// write(0, 0, 0, &buffer);
|
|
||||||
/// }
|
|
||||||
|
|
||||||
pub fn write(&mut self, drive: u8, block: BlockIndex, buf: &[u8]) {
|
|
||||||
assert!(buf.len() == 512);
|
|
||||||
self.setup(drive, block);
|
|
||||||
self.write_command(Command::Write);
|
|
||||||
self.busy_loop();
|
|
||||||
for i in 0..256 {
|
|
||||||
let mut data = 0_u16;
|
|
||||||
data.set_bits(0..8, buf[i * 2] as u16);
|
|
||||||
data.set_bits(8..16, buf[i * 2 + 1] as u16);
|
|
||||||
|
|
||||||
//log!("Data: 0x{:04X} | {}{} \n", data, buf[i * 2] as char, buf[i * 2 + 1] as char);
|
|
||||||
|
|
||||||
self.write_data(data);
|
|
||||||
}
|
|
||||||
self.busy_loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref BUSES: Mutex<Vec<Bus>> = Mutex::new(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disk_size(sectors: u32) -> (u32, String) {
|
|
||||||
let bytes = sectors * 512;
|
|
||||||
if bytes >> 20 < 1000 {
|
|
||||||
(bytes >> 20, String::from("MB"))
|
|
||||||
} else {
|
|
||||||
(bytes >> 30, String::from("GB"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list() -> Vec<(u8, u8, String, String, u32, String, u32)> {
|
|
||||||
let mut buses = BUSES.lock();
|
|
||||||
let mut res = Vec::new();
|
|
||||||
for bus in 0..2 {
|
|
||||||
for drive in 0..2 {
|
|
||||||
if let Some(buf) = buses[bus as usize].identify_drive(drive) {
|
|
||||||
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();
|
|
||||||
let sectors = (buf[61] as u32) << 16 | (buf[60] as u32);
|
|
||||||
let (size, unit) = disk_size(sectors);
|
|
||||||
res.push((bus, drive, model, serial, size, unit, sectors));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identify a specific drive on a bus, format: (bus, drive, model, serial. size, unit, sectors)
|
|
||||||
pub fn indentify_drive(bus: u8, drive: u8) -> Option<(u8, u8, String, String, u32, String, u32)> {
|
|
||||||
let mut buses = BUSES.lock();
|
|
||||||
if let Some(buf) = buses[bus as usize].identify_drive(drive) {
|
|
||||||
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();
|
|
||||||
let sectors = (buf[61] as u32) << 16 | (buf[60] as u32);
|
|
||||||
let (size, unit) = disk_size(sectors);
|
|
||||||
Some((bus, drive, model, serial, size, unit, sectors))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(bus: u8, drive: u8, block: BlockIndex, buf: &mut [u8]) {
|
|
||||||
let mut buses = BUSES.lock();
|
|
||||||
trace!("Reading Block 0x{:08X}\n", block);
|
|
||||||
buses[bus as usize].read(drive, block, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(bus: u8, drive: u8, block: BlockIndex, buf: &[u8]) {
|
|
||||||
let mut buses = BUSES.lock();
|
|
||||||
//log!("Writing Block 0x{:08X}\n", block);
|
|
||||||
buses[bus as usize].write(drive, block, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drive_is_present(bus: usize) -> bool {
|
|
||||||
unsafe { BUSES.lock()[bus].status_register.read() != 0xFF }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() -> Result<(), ()> {
|
|
||||||
{
|
|
||||||
let mut buses = BUSES.lock();
|
|
||||||
buses.push(Bus::new(0, 0x1F0, 0x3F6, 14));
|
|
||||||
buses.push(Bus::new(1, 0x170, 0x376, 15));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
Reference in a new issue