Kernel: Add a timer and keyboard interface
This commit is contained in:
parent
793b24a994
commit
ad4a83f0a4
@ -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"
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
2
moon/src/arch/timer.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub use super::x86_64::timer::*;
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -2,4 +2,5 @@
|
||||
pub mod cpu;
|
||||
pub mod io;
|
||||
pub mod interrupts;
|
||||
pub mod timer;
|
||||
mod gdt;
|
24
moon/src/arch/x86_64/timer.rs
Normal file
24
moon/src/arch/x86_64/timer.rs
Normal 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;
|
||||
}
|
@ -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
112
moon/src/timer.rs
Normal 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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user