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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
pc-keyboard = "0.6.1"
|
||||||
|
pic8259 = "0.10.2"
|
||||||
spin = "0.9.4"
|
spin = "0.9.4"
|
||||||
|
volatile = "0.4.5"
|
||||||
x86_64 = "0.14.10"
|
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::*;
|
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 cpu;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod interrupts;
|
pub mod interrupts;
|
||||||
|
pub mod timer;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod 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)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use core::arch::x86_64::{__cpuid as do_cpuid, CpuidResult};
|
use core::arch::x86_64::{__cpuid as do_cpuid, CpuidResult};
|
||||||
use core::arch::asm;
|
|
||||||
|
|
||||||
use super::gdt;
|
use super::gdt;
|
||||||
|
|
||||||
@ -15,23 +14,24 @@ pub fn get_processor_id() -> u32 {
|
|||||||
return cpuid_result.ebx >> 24;
|
return cpuid_result.ebx >> 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use x86_64::instructions::hlt;
|
||||||
|
use x86_64::instructions::interrupts;
|
||||||
|
|
||||||
pub fn halt() -> ! {
|
pub fn halt() -> ! {
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
interrupts::disable();
|
||||||
asm!("cli");
|
wait_for_interrupt();
|
||||||
asm!("hlt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for_interrupt()
|
pub fn wait_for_interrupt()
|
||||||
{
|
{
|
||||||
unsafe { asm!("hlt"); }
|
hlt();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn breakpoint()
|
pub fn breakpoint()
|
||||||
{
|
{
|
||||||
unsafe { asm!("int3"); }
|
interrupts::int3();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn platform_init()
|
pub fn platform_init()
|
||||||
|
@ -1,10 +1,26 @@
|
|||||||
|
#![cfg(target_arch = "x86_64")]
|
||||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
||||||
use crate::try_println;
|
use crate::{try_print, try_println};
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use spin::Mutex;
|
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());
|
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)
|
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame)
|
||||||
{
|
{
|
||||||
try_println!("Breakpoint!\n{:#?}", stack_frame);
|
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);
|
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()
|
pub fn load()
|
||||||
{
|
{
|
||||||
let mut idt = IDT.lock();
|
let mut idt = IDT.lock();
|
||||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||||
idt.page_fault.set_handler_fn(page_fault_handler);
|
idt.page_fault.set_handler_fn(page_fault_handler);
|
||||||
idt.double_fault.set_handler_fn(double_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 {
|
unsafe {
|
||||||
idt.load_unsafe();
|
idt.load_unsafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
PICS.lock().initialize();
|
||||||
|
PICS.lock().write_masks(0b11111100, 0b11111111)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable()
|
pub fn enable()
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod interrupts;
|
pub mod interrupts;
|
||||||
|
pub mod timer;
|
||||||
mod gdt;
|
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 init;
|
||||||
mod arch;
|
mod arch;
|
||||||
mod log;
|
mod log;
|
||||||
|
mod timer;
|
||||||
|
|
||||||
use arch::cpu;
|
use arch::cpu;
|
||||||
use arch::interrupts;
|
use arch::interrupts;
|
||||||
@ -27,6 +28,8 @@ pub extern "C" fn _start() -> ! {
|
|||||||
|
|
||||||
cpu::platform_init();
|
cpu::platform_init();
|
||||||
|
|
||||||
|
timer::init(timer::decode_boot_timestamp());
|
||||||
|
|
||||||
interrupts::load();
|
interrupts::load();
|
||||||
|
|
||||||
video::clear(Color::White);
|
video::clear(Color::White);
|
||||||
@ -35,10 +38,5 @@ pub extern "C" fn _start() -> ! {
|
|||||||
|
|
||||||
interrupts::enable();
|
interrupts::enable();
|
||||||
|
|
||||||
cpu::breakpoint();
|
cpu::idle_loop();
|
||||||
|
|
||||||
println!("Still here!");
|
|
||||||
|
|
||||||
cpu::halt();
|
|
||||||
|
|
||||||
}
|
}
|
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