Add system clock and initial work on ATA
This commit is contained in:
parent
6e8ebde058
commit
08597721f8
87
src/ata.rs
Normal file
87
src/ata.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// ATA Driver!
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::hint::spin_loop;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
|
||||
|
||||
// Commands to send to the drives
|
||||
#[repr(u16)]
|
||||
enum Command {
|
||||
Read = 0x20,
|
||||
Write = 0x30,
|
||||
Identify = 0xEC,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)]
|
||||
enum Status {
|
||||
ERR = 0,
|
||||
IDX,
|
||||
CORR,
|
||||
DRQ,
|
||||
SRV,
|
||||
DF,
|
||||
RDY,
|
||||
BSY,
|
||||
}
|
||||
|
||||
#[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_blockless_register: PortReadOnly<u8>
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Bus {
|
||||
pub fn new(id: u8, io_base: u16, ctrl_base: u16, irq: u8) -> Self {
|
||||
Bus {
|
||||
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_blockless_register: PortReadOnly::new(ctrl_base + 1),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lazy_static! {
|
||||
pub static ref BUSES: Mutex<Vec<Bus>> = Mutex::new(Vec::new());
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let mut buses = BUSES.lock();
|
||||
buses.push(Bus::new(0, 0x1f0, 0x3f6, 14));
|
||||
buses.push(Bus::new(1, 0x170, 0x376, 15));
|
||||
}
|
||||
|
63
src/clock.rs
Normal file
63
src/clock.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
static CLOCKS_PER_NANOSECOND: AtomicU64 = AtomicU64::new(0);
|
||||
static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
|
||||
const PIT_FREQUENCY: f64 = 3_579_545.0 / 3.0;
|
||||
const PIT_DIVIDER: usize = 1193;
|
||||
const PIT_INTERVAL: f64 = (PIT_DIVIDER as f64) / PIT_FREQUENCY;
|
||||
|
||||
fn rdtsc() -> u64 {
|
||||
unsafe {
|
||||
core::arch::x86_64::_mm_lfence();
|
||||
core::arch::x86_64::_rdtsc()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_pit_freqency_divider(divider: u16, channel: u8) {
|
||||
use x86_64::instructions::interrupts;
|
||||
|
||||
interrupts::without_interrupts(|| {
|
||||
let bytes = divider.to_le_bytes();
|
||||
let mut cmd: Port<u8> = Port::new(0x43);
|
||||
let mut data: Port<u8> = Port::new(0x40 + channel as u16);
|
||||
let operating_mode = 6;
|
||||
let access_mode = 3;
|
||||
unsafe {
|
||||
cmd.write((channel << 6) | (access_mode << 4) | operating_mode);
|
||||
data.write(bytes[0]);
|
||||
data.write(bytes[1]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn ticks() -> usize {
|
||||
PIT_TICKS.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn uptime() -> f64 {
|
||||
PIT_INTERVAL * ticks() as f64
|
||||
}
|
||||
|
||||
pub fn sleep(seconds: f64) {
|
||||
let start = uptime();
|
||||
while uptime() - start < seconds {
|
||||
x86_64::instructions::interrupts::enable_and_hlt();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pit_interrupt_handler() {
|
||||
PIT_TICKS.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let divider = if PIT_DIVIDER < 65535 { PIT_DIVIDER } else { 0 };
|
||||
let channel = 0;
|
||||
set_pit_freqency_divider(divider as u16, channel);
|
||||
|
||||
let calibration_time = 250_000;
|
||||
let a = rdtsc();
|
||||
sleep(calibration_time as f64 / 1e6);
|
||||
let b = rdtsc();
|
||||
CLOCKS_PER_NANOSECOND.store((b - a) / calibration_time, Ordering::Relaxed);
|
||||
}
|
|
@ -39,6 +39,7 @@ extern "x86-interrupt" fn double_fault_handler(
|
|||
}
|
||||
|
||||
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
crate::clock::pit_interrupt_handler();
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||
|
|
38
src/main.rs
38
src/main.rs
|
@ -8,6 +8,8 @@
|
|||
#![feature(alloc_error_handler)]
|
||||
|
||||
mod allocator;
|
||||
mod ata;
|
||||
mod clock;
|
||||
mod gdt;
|
||||
mod interrupts;
|
||||
mod memory;
|
||||
|
@ -35,34 +37,38 @@ fn init() {
|
|||
interrupts::init();
|
||||
}
|
||||
|
||||
macro_rules! status {
|
||||
($n:expr) => {
|
||||
print!("[ ");
|
||||
change_color(Color::Green, Color::Black);
|
||||
print!("OK");
|
||||
change_color(Color::White, Color::Black);
|
||||
println!(" ] {}", $n);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start(boot_info: &'static BootInfo) {
|
||||
use crate::vga_buffer::{change_color, Color};
|
||||
use memory::BootInfoFrameAllocator;
|
||||
use x86_64::VirtAddr;
|
||||
use crate::vga_buffer::{change_color, Color}; // For status! macro
|
||||
|
||||
init();
|
||||
print!("[ ");
|
||||
change_color(Color::Green, Color::Black);
|
||||
print!("OK");
|
||||
change_color(Color::White, Color::Black);
|
||||
println!(" ] Initialized GDT and interrupts");
|
||||
status!("Initialized GDT and Interrupts");
|
||||
|
||||
clock::init();
|
||||
status!("Initialized system clock");
|
||||
|
||||
let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset);
|
||||
let mut mapper = unsafe { memory::init(phys_mem_offset) };
|
||||
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) };
|
||||
print!("[ ");
|
||||
change_color(Color::Green, Color::Black);
|
||||
print!("OK");
|
||||
change_color(Color::White, Color::Black);
|
||||
println!(" ] Initialized Mapper and Frame allocator");
|
||||
status!("Initialized Mapper and Frame allocator");
|
||||
|
||||
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("Heap initialization failed");
|
||||
print!("[ ");
|
||||
change_color(Color::Green, Color::Black);
|
||||
print!("OK");
|
||||
change_color(Color::White, Color::Black);
|
||||
println!(" ] Initialized heap");
|
||||
status!("Initialized heap");
|
||||
|
||||
// Must be initialized AFTER the heap!
|
||||
ata::init();
|
||||
|
||||
println!();
|
||||
print!("Welcome to ");
|
||||
|
|
14
src/shell.rs
14
src/shell.rs
|
@ -17,6 +17,7 @@ pub fn evaluate(command: &str) {
|
|||
"echo" => echo,
|
||||
"shutdown" => shutdown,
|
||||
"clear" => clear,
|
||||
"uptime" => uptime,
|
||||
_ => default,
|
||||
};
|
||||
selected(&parts[..]);
|
||||
|
@ -68,7 +69,7 @@ fn compute_edit_distance(a: &str, b: &str) -> usize {
|
|||
fn default(arguments: &[&str]) {
|
||||
let mut distances: Vec<(&str, usize)> = Vec::new();
|
||||
let curr = arguments[0];
|
||||
for &command in &["help", "info", "echo", "shutdown", "clear"] {
|
||||
for &command in &["help", "info", "echo", "shutdown", "clear", "uptime"] {
|
||||
let distance = compute_edit_distance(curr, command);
|
||||
distances.push((command, distance));
|
||||
}
|
||||
|
@ -81,11 +82,12 @@ fn default(arguments: &[&str]) {
|
|||
fn help(_arguments: &[&str]) {
|
||||
change_color(Color::LightBlue, Color::Black);
|
||||
print!("KarxShell help menu\n\n");
|
||||
println!("[clear] Clears the screen");
|
||||
println!("[echo <arguments>] Echoes whatever arguments you pass in");
|
||||
println!("[help] This message");
|
||||
println!("[info] Info about KarxOS");
|
||||
println!("[echo <arguments>] Echoes whatever arguments you pass in");
|
||||
println!("[shutdown] Shuts off the system (QEMU only)");
|
||||
println!("[clear] Clears the screen");
|
||||
println!("[uptime] Get the system uptime");
|
||||
change_color(Color::White, Color::Black);
|
||||
}
|
||||
|
||||
|
@ -141,3 +143,9 @@ fn clear(_arguments: &[&str]) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn uptime(_arguments: &[&str]) {
|
||||
use crate::clock::uptime;
|
||||
|
||||
println!("Uptime: {:.2} seconds", uptime());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue