Add system clock and initial work on ATA

This commit is contained in:
Yash Karandikar 2021-09-23 11:29:49 -05:00
parent 6e8ebde058
commit 08597721f8
5 changed files with 184 additions and 19 deletions

87
src/ata.rs Normal file
View 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
View 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);
}

View file

@ -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());

View file

@ -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 ");

View file

@ -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());
}