Kernel: Add a timer and keyboard interface

This commit is contained in:
apio 2022-11-01 15:00:23 +01:00
parent 793b24a994
commit ad4a83f0a4
10 changed files with 224 additions and 17 deletions

View File

@ -16,5 +16,8 @@ strip = "debuginfo"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pc-keyboard = "0.6.1"
pic8259 = "0.10.2"
spin = "0.9.4"
volatile = "0.4.5"
x86_64 = "0.14.10"

View File

@ -1,2 +1,13 @@
#![cfg(target_arch = "x86_64")]
#![allow(dead_code)]
#[cfg(target_arch = "x86_64")]
pub use super::x86_64::cpu::*;
use super::interrupts;
pub fn idle_loop() -> ! {
interrupts::enable();
loop {
wait_for_interrupt();
}
}

View File

@ -1,6 +1,7 @@
pub mod cpu;
pub mod io;
pub mod interrupts;
pub mod timer;
#[cfg(target_arch = "x86_64")]
mod x86_64;

2
moon/src/arch/timer.rs Normal file
View File

@ -0,0 +1,2 @@
#[cfg(target_arch = "x86_64")]
pub use super::x86_64::timer::*;

View File

@ -2,7 +2,6 @@
#![allow(dead_code)]
use core::arch::x86_64::{__cpuid as do_cpuid, CpuidResult};
use core::arch::asm;
use super::gdt;
@ -15,23 +14,24 @@ pub fn get_processor_id() -> u32 {
return cpuid_result.ebx >> 24;
}
use x86_64::instructions::hlt;
use x86_64::instructions::interrupts;
pub fn halt() -> ! {
loop {
unsafe {
asm!("cli");
asm!("hlt");
}
interrupts::disable();
wait_for_interrupt();
}
}
pub fn wait_for_interrupt()
{
unsafe { asm!("hlt"); }
hlt();
}
pub fn breakpoint()
{
unsafe { asm!("int3"); }
interrupts::int3();
}
pub fn platform_init()

View File

@ -1,10 +1,26 @@
#![cfg(target_arch = "x86_64")]
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
use crate::try_println;
use crate::{try_print, try_println};
use core::arch::asm;
use spin::Mutex;
use crate::timer;
use pic8259::ChainedPics;
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: Mutex<ChainedPics> =
Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
static IDT: Mutex<InterruptDescriptorTable> = Mutex::new(InterruptDescriptorTable::new());
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum IRQ {
Timer = PIC_1_OFFSET,
Keyboard = PIC_1_OFFSET + 1,
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame)
{
try_println!("Breakpoint!\n{:#?}", stack_frame);
@ -19,16 +35,55 @@ extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame,
panic!("Double fault!\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame)
{
timer::tick();
unsafe { PICS.lock().notify_end_of_interrupt(IRQ::Timer as u8); }
}
extern "x86-interrupt" fn keyboard_handler(_stack_frame: InterruptStackFrame)
{
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use x86_64::instructions::port::Port;
static KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(Keyboard::new(HandleControl::Ignore));
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => try_print!("{}", character),
DecodedKey::RawKey(key) => try_print!("{:?}", key),
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(IRQ::Keyboard as u8);
}
}
pub fn load()
{
let mut idt = IDT.lock();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.page_fault.set_handler_fn(page_fault_handler);
idt.double_fault.set_handler_fn(double_fault_handler);
idt[IRQ::Timer as usize].set_handler_fn(timer_handler);
idt[IRQ::Keyboard as usize].set_handler_fn(keyboard_handler);
unsafe {
idt.load_unsafe();
}
unsafe {
PICS.lock().initialize();
PICS.lock().write_masks(0b11111100, 0b11111111)
}
}
pub fn enable()

View File

@ -2,4 +2,5 @@
pub mod cpu;
pub mod io;
pub mod interrupts;
pub mod timer;
mod gdt;

View File

@ -0,0 +1,24 @@
#![cfg(target_arch = "x86_64")]
pub const TIMER_FREQUENCY: u64 = 5000;
const BASE_FREQUENCY: u32 = 1193182;
use x86_64::instructions::port::Port;
use crate::arch::io;
pub fn platform_init()
{
let mut divisor = (BASE_FREQUENCY / TIMER_FREQUENCY as u32) as u16;
if divisor < 100 { divisor = 100; }
let mut port: Port<u8> = Port::new(0x40);
unsafe {
port.write((divisor & 0xff) as u8);
io::io_delay();
port.write(((divisor & 0xFF00) >> 8) as u8);
}
}
pub fn frequency() -> u64 {
return TIMER_FREQUENCY;
}

View File

@ -11,6 +11,7 @@ mod util;
mod init;
mod arch;
mod log;
mod timer;
use arch::cpu;
use arch::interrupts;
@ -27,6 +28,8 @@ pub extern "C" fn _start() -> ! {
cpu::platform_init();
timer::init(timer::decode_boot_timestamp());
interrupts::load();
video::clear(Color::White);
@ -35,10 +38,5 @@ pub extern "C" fn _start() -> ! {
interrupts::enable();
cpu::breakpoint();
println!("Still here!");
cpu::halt();
cpu::idle_loop();
}

112
moon/src/timer.rs Normal file
View File

@ -0,0 +1,112 @@
#![allow(dead_code)]
use crate::println;
use spin::RwLock;
static mut TIMER: u64 = 0; // writes to aligned 64-bit integers are atomic, at least on x86_64
static BOOT_TIMESTAMP: RwLock<i64> = RwLock::new(0);
use crate::arch::timer;
pub fn init(boot_timestamp: i64)
{
*BOOT_TIMESTAMP.write() = boot_timestamp;
timer::platform_init();
}
pub fn tick()
{
unsafe { TIMER += 1 }
}
pub fn count() -> u64 {
unsafe { TIMER }
}
const MS_PER_SECOND: u64 = 1000;
const US_PER_SECOND: u64 = 1000000;
const NS_PER_SECOND: u64 = 1000000000;
pub fn second_count() -> u64 {
return count() / timer::frequency();
}
// FIXME: Propagate overflows. A system call may want to return EOVERFLOW instead of panicking.
pub fn millisecond_count() -> u64 {
return count().checked_mul(MS_PER_SECOND).unwrap() / timer::frequency();
}
pub fn microsecond_count() -> u64 {
return count().checked_mul(US_PER_SECOND).unwrap() / timer::frequency();
}
pub fn nanosecond_count() -> u64 {
return count().checked_mul(NS_PER_SECOND).unwrap() / timer::frequency();
}
pub fn boot_timestamp() -> i64 {
*BOOT_TIMESTAMP.read()
}
// FIXME: These functions are verbose.
pub fn second_time() -> i64 {
i64::try_from(second_count()).unwrap().checked_add(boot_timestamp()).unwrap()
}
pub fn millisecond_time() -> i64 {
i64::try_from(millisecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(MS_PER_SECOND as i64).unwrap()).unwrap()
}
pub fn microsecond_time() -> i64 {
i64::try_from(microsecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(US_PER_SECOND as i64).unwrap()).unwrap()
}
pub fn nanosecond_time() -> i64 {
i64::try_from(nanosecond_count()).unwrap().checked_add(boot_timestamp().checked_mul(NS_PER_SECOND as i64).unwrap()).unwrap()
}
pub fn isleap(year: i32) -> bool
{
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
fn make_yday(year: i32, month: i32) -> i32 {
let upto: [i16; 12usize] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
let mut yd: i16;
yd = upto[(month - 1) as usize];
if month > 2 && isleap(year)
{
yd += 1;
}
yd as i32
}
pub fn broken_down_to_unix(year: i64, month: i64, day: i64, hour: i64, min: i64, sec: i64) -> i64 {
return sec + min * 60 + hour * 3600 + (make_yday(year as i32, month as i32) + (day - 1) as i32) as i64 * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 -
((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400;
}
fn bcd_number_to_decimal(num: u8) -> i32
{
return (((num >> 4) * 10) + (num & 0xf)) as i32;
}
pub fn decode_timestamp(datetime: [u8; 8]) -> i64 {
let year = (bcd_number_to_decimal(datetime[0]) * 100) + bcd_number_to_decimal(datetime[1]);
let month = bcd_number_to_decimal(datetime[2]);
let day = bcd_number_to_decimal(datetime[3]);
let hour = bcd_number_to_decimal(datetime[4]);
let min = bcd_number_to_decimal(datetime[5]);
let sec = bcd_number_to_decimal(datetime[6]);
println!("{}-{}-{} {}:{}:{}", year, month, day, hour, min, sec);
broken_down_to_unix((year - 1900) as i64, month as i64, day as i64, hour as i64, min as i64, sec as i64)
}
use crate::bootboot::BOOTBOOT;
use crate::util::get_bootboot;
pub fn decode_boot_timestamp() -> i64 {
let boot: &BOOTBOOT = get_bootboot();
decode_timestamp(boot.datetime)
}