Skip to content

Commit

Permalink
Kernel+Arch: Impl PIT timer interrupt
Browse files Browse the repository at this point in the history
  • Loading branch information
corigan01 committed Jan 12, 2025
1 parent 7eb3063 commit 74b54e7
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 4 deletions.
30 changes: 30 additions & 0 deletions crates/arch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod idt64;
pub mod io;
pub mod paging64;
pub mod pic8259;
pub mod pit825x;
pub mod registers;

pub mod interrupts {
Expand All @@ -43,6 +44,35 @@ pub mod interrupts {
pub unsafe fn disable_interrupts() {
core::arch::asm!("cli");
}

pub fn assert_interrupts(enabled: bool) {
let int_state = crate::registers::eflags::is_interrupts_enable_set();
assert_eq!(
int_state,
enabled,
"Interrupts were {} when expected them to be {}",
if int_state { "on" } else { "off" },
if enabled { "on" } else { "off" }
);
}

#[macro_export]
macro_rules! critcal_section {
($($tt:tt)*) => {
let _priv_interrupt_before_state =
::arch::registers::eflags::is_interrupts_enable_set();

if _priv_interrupt_before_state {
unsafe { ::arch::interrupts::disable_interrupts() };
}

$($tt)*;

if _priv_interrupt_before_state {
unsafe { ::arch::interrupts::enable_interrupts() };
}
};
}
}

#[derive(Clone, Copy, Debug)]
Expand Down
104 changes: 104 additions & 0 deletions crates/arch/src/pit825x.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
____ __ __ _ __
/ __ \__ _____ ____ / /___ ____ _ / / (_) /
/ /_/ / // / _ `/ _ \/ __/ // / ' \ / /__/ / _ \
\___\_\_,_/\_,_/_//_/\__/\_,_/_/_/_/ /____/_/_.__/
Part of the Quantum OS Project
Copyright 2025 Gavin Kellam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use crate::{interrupts::assert_interrupts, io::IOPort};

const CHANNEL_0_DATA: IOPort = IOPort::new(0x40);
const _CHANNEL_1_DATA: IOPort = IOPort::new(0x41);
const _CHANNEL_2_DATA: IOPort = IOPort::new(0x42);
const COMMAND: IOPort = IOPort::new(0x43);

#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum PitSelectChannel {
Channel0 = 0,
Channel1 = 1,
Channel2 = 2,
ReadBack = 3,
}

#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum PitAccessMode {
LatchCount = 0,
AccessLoOnly = 1,
AccessHiOnly = 2,
AccessLoHi = 3,
}

#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum PitOperatingMode {
TerminalCount = 0,
RetriggerableOneShot = 1,
RateGenerator = 2,
SquareWave = 3,
SoftwareStrobe = 4,
HardwareStrobe = 5,
}

pub fn pit_command(
channel: PitSelectChannel,
access: PitAccessMode,
mode: PitOperatingMode,
bcd_mode: bool,
) {
let bcd_bit = if bcd_mode { 1 } else { 0 };
let mode_bit = (mode as u8) << 1;
let access_bit = (access as u8) << 4;
let channel_bit = (channel as u8) << 6;

unsafe {
COMMAND.write_byte(bcd_bit | mode_bit | access_bit | channel_bit);
}
}

/// Set the pit reload count.
///
/// # Interrupts
/// Interrupts must be disabled before calling this function!
pub fn set_pit_reload(count: u16) {
assert_interrupts(false);

unsafe {
CHANNEL_0_DATA.write_byte((count & 0xFF) as u8);
CHANNEL_0_DATA.write_byte(((count >> 8) & 0xFF) as u8);
}
}

/// Set the pit reload count in HZ.
///
/// # Interrupts
/// Interrupts must be disabled before calling this function!
pub fn set_pit_hz(hz: f32) -> f32 {
assert_interrupts(false);

let div = 1193182_f32 / hz;
let int_div = div as u16;

set_pit_reload(int_div);

1193182_f32 / (int_div as f32)
}
21 changes: 19 additions & 2 deletions crates/arch/src/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,12 @@ pub mod cr4 {
pub mod eflags {
#[inline(never)]
pub fn read() -> u32 {
let mut flags;
#[cfg(not(target_pointer_width = "64"))]
let mut flags: u32;
#[cfg(target_pointer_width = "64")]
let mut flags: u64;

#[cfg(not(target_pointer_width = "64"))]
unsafe {
core::arch::asm!("
pushf
Expand All @@ -394,7 +398,20 @@ pub mod eflags {
)
}

flags
#[cfg(target_pointer_width = "64")]
unsafe {
core::arch::asm!("
pushf
pop rax
",
out("rax") flags
)
}

#[cfg(not(target_pointer_width = "64"))]
return flags;
#[cfg(target_pointer_width = "64")]
return flags as u32;
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/mem/src/vmm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FO
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

pub struct Vmm {}
30 changes: 28 additions & 2 deletions kernel/src/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA
*/

use arch::{
attach_irq,
attach_irq, critcal_section,
idt64::{
ExceptionKind, InterruptDescTable, InterruptFlags, InterruptInfo, fire_debug_int, interrupt,
},
Expand All @@ -34,16 +34,19 @@ use arch::{
use lldebug::{logln, sync::Mutex};

static INTERRUPT_TABLE: Mutex<InterruptDescTable> = Mutex::new(InterruptDescTable::new());
static IRQ_HANDLERS: Mutex<[Option<fn(&InterruptInfo)>; 32]> = Mutex::new([None; 32]);

#[interrupt(0..256)]
fn main_handler(args: InterruptInfo) {
match args.flags {
// IRQ
InterruptFlags::Irq(irq_num) if irq_num - PIC_IRQ_OFFSET <= 16 => {
call_attached_irq(irq_num - PIC_IRQ_OFFSET, &args);
unsafe { pic_eoi(irq_num - PIC_IRQ_OFFSET) };
}
InterruptFlags::Debug => (),
exception => {
logln!("{:#?}", exception)
panic!("UNHANDLED FAULT\n{:#016x?}", args)
}
_ => (),
}
Expand All @@ -53,6 +56,29 @@ fn main_handler(args: InterruptInfo) {
}
}

fn call_attached_irq(irq_id: u8, args: &InterruptInfo) {
let irq_handler = IRQ_HANDLERS.lock();

if let Some(handler) = irq_handler
.get((irq_id) as usize)
.and_then(|&handler| handler)
{
handler(args);
}
}

/// Set a function to be called whenever an irq is triggered.
pub fn attach_irq_handler(handler_fn: fn(&InterruptInfo), irq: u8) {
critcal_section! {
let mut irq_handler = IRQ_HANDLERS.lock();
let Some(handler) = irq_handler.get_mut(irq as usize) else {
return;
};

*handler = Some(handler_fn);
}
}

pub fn attach_interrupts() {
let mut idt = INTERRUPT_TABLE.lock();
attach_irq!(idt, main_handler);
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA

mod int;
mod panic;
mod timer;
extern crate alloc;

use bootloader::KernelBootHeader;
Expand Down Expand Up @@ -66,6 +67,7 @@ fn main(kbh: &KernelBootHeader) {

int::attach_interrupts();
int::enable_pic();
timer::init_timer();

logln!(
"Init Heap Region ({})",
Expand Down
57 changes: 57 additions & 0 deletions kernel/src/timer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
____ __ __ __ __
/ __ \__ _____ ____ / /___ ____ _ / //_/__ _______ ___ / /
/ /_/ / // / _ `/ _ \/ __/ // / ' \ / ,< / -_) __/ _ \/ -_) /
\___\_\_,_/\_,_/_//_/\__/\_,_/_/_/_/ /_/|_|\__/_/ /_//_/\__/_/
Part of the Quantum OS Kernel
Copyright 2025 Gavin Kellam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use arch::{
critcal_section,
idt64::InterruptInfo,
pit825x::{PitAccessMode, PitOperatingMode, PitSelectChannel, pit_command, set_pit_hz},
};
use lldebug::{log, logln};

use crate::int::attach_irq_handler;

const TIMER_HZ: f32 = 1000_f32;

pub fn init_timer() {
log!("Enabling PIT...");
critcal_section! {
// Put the pit in repeted trigger mode
pit_command(
PitSelectChannel::Channel0,
PitAccessMode::AccessLoHi,
PitOperatingMode::SquareWave,
false,
);

// Set the trigger time
logln!("({}Hz)", set_pit_hz(TIMER_HZ));

// Attach our IRQ
attach_irq_handler(pit_interrupt_handler, 0);
}
logln!("OK");
}

fn pit_interrupt_handler(_args: &InterruptInfo) {}

0 comments on commit 74b54e7

Please sign in to comment.