finish PIT Timer and add testcase

This commit is contained in:
sdww0 2022-11-09 20:33:41 +08:00
parent 492022cbeb
commit 280591db66
12 changed files with 394 additions and 39 deletions

View File

@ -4,9 +4,13 @@ pub mod framebuffer;
mod io_port;
pub mod pci;
pub mod serial;
mod pic;
pub use pic::{TimerCallback, TIMER_FREQ};
pub(crate) use pic::{add_timeout_list,TICK};
pub use self::io_port::IoPort;
pub(crate) fn init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
framebuffer::init(framebuffer);
pic::init();
}

View File

@ -0,0 +1,182 @@
use crate::cell::Cell;
use crate::x86_64_util::out8;
use crate::{IrqAllocateHandle, TrapFrame};
use alloc::sync::Arc;
use alloc::vec::Vec;
use alloc::{boxed::Box, collections::BinaryHeap};
use core::any::Any;
use lazy_static::lazy_static;
use spin::Mutex;
const MASTER_CMD: u16 = 0x20;
const MASTER_DATA: u16 = MASTER_CMD + 1;
const SLAVE_CMD: u16 = 0xA0;
const SLAVE_DATA: u16 = SLAVE_CMD + 1;
const TIMER_RATE: u32 = 1193182;
/// This value represent the base timer frequency in Hz
pub const TIMER_FREQ: u64 = 100;
const TIMER_PERIOD_IO_PORT: u16 = 0x40;
const TIMER_MODE_IO_PORT: u16 = 0x43;
const TIMER_SQUARE_WAVE: u8 = 0x36;
const TIMER_IRQ_NUM: u8 = 32;
pub static mut TICK: u64 = 0;
lazy_static! {
static ref TIMER_IRQ: Mutex<IrqAllocateHandle> = Mutex::new(
crate::trap::allocate_target_irq(TIMER_IRQ_NUM).expect("Timer irq Allocate error")
);
}
pub fn init() {
// Start initialization
out8(MASTER_CMD, 0x11);
out8(SLAVE_CMD, 0x11);
// Set offsets
out8(MASTER_DATA, 0x20);
out8(SLAVE_DATA, 0x28);
// Set up cascade
out8(MASTER_DATA, 4);
out8(SLAVE_DATA, 2);
// Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
out8(MASTER_DATA, 1);
out8(SLAVE_DATA, 1);
// Unmask interrupts
out8(MASTER_DATA, 0);
out8(SLAVE_DATA, 0);
// Ack remaining interrupts
out8(MASTER_CMD, 0x20);
out8(SLAVE_CMD, 0x20);
// Initialize timer.
let cycle = TIMER_RATE / TIMER_FREQ as u32; // 1ms per interrupt.
out8(TIMER_MODE_IO_PORT, TIMER_SQUARE_WAVE);
out8(TIMER_PERIOD_IO_PORT, (cycle & 0xFF) as _);
out8(TIMER_PERIOD_IO_PORT, (cycle >> 8) as _);
TIMER_IRQ.lock().on_active(timer_callback);
}
#[inline(always)]
fn ack() {
out8(MASTER_CMD, 0x20);
}
fn timer_callback(trap_frame: &TrapFrame) {
// FIXME: disable and enable interupt will cause infinity loop
// x86_64_util::disable_interrupts();
ack();
let current_ms;
unsafe {
current_ms = TICK;
TICK += 1;
}
let timeout_list = TIMEOUT_LIST.get();
let mut callbacks : Vec<Arc<TimerCallback>>= Vec::new();
while let Some(t) = timeout_list.peek() {
if t.expire_ms <= current_ms {
callbacks.push(timeout_list.pop().unwrap());
} else {
break;
}
}
for callback in callbacks{
if callback.is_enable(){
callback.callback.call((&callback,));
}
}
// x86_64_util::enable_interrupts();
}
lazy_static! {
static ref TIMEOUT_LIST: Cell<BinaryHeap<Arc<TimerCallback>>> = Cell::new(BinaryHeap::new()) ;
}
pub struct TimerCallback {
expire_ms: u64,
data: Arc<dyn Any + Send + Sync>,
callback: Box<dyn Fn(&TimerCallback) + Send + Sync>,
enable: Cell<bool>,
}
impl TimerCallback {
fn new(
timeout_ms: u64,
data: Arc<dyn Any + Send + Sync>,
callback: Box<dyn Fn(&TimerCallback) + Send + Sync>,
) -> Self {
Self {
expire_ms: timeout_ms,
data,
callback,
enable:Cell::new(true),
}
}
pub fn data(&self) -> &Arc<dyn Any + Send + Sync> {
&self.data
}
/// disable this timeout
pub fn disable(&self){
*self.enable.get() = false;
}
/// enable this timeout
pub fn enable(&self){
*self.enable.get() = true;
}
pub fn is_enable(&self) -> bool{
*self.enable
}
}
impl PartialEq for TimerCallback {
fn eq(&self, other: &Self) -> bool {
self.expire_ms == other.expire_ms
}
}
impl Eq for TimerCallback {}
impl PartialOrd for TimerCallback {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TimerCallback {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.expire_ms.cmp(&other.expire_ms).reverse()
}
}
/// add timeout task into timeout list, the frequency see const TIMER_FREQ
///
/// user should ensure that the callback function cannot take too much time
///
pub fn add_timeout_list<F, T>(timeout: u64, data: T, callback: F) -> Arc<TimerCallback>
where
F: Fn(&TimerCallback) + Send + Sync + 'static,
T: Any + Send + Sync,
{
unsafe {
let timer_callback = TimerCallback::new(
TICK + timeout,
Arc::new(data),
Box::new(callback),
);
let arc = Arc::new(timer_callback);
TIMEOUT_LIST.get().push(arc.clone());
arc
}
}

View File

@ -62,10 +62,6 @@ pub use crate::serial_println as println;
pub fn init(boot_info: &'static mut BootInfo) {
let siz = boot_info.framebuffer.as_ref().unwrap() as *const FrameBuffer as usize;
device::init(boot_info.framebuffer.as_mut().unwrap());
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
trap::init();
enable_common_cpu_features();
let mut memory_init = false;
// memory
for region in boot_info.memory_regions.iter() {
@ -84,13 +80,19 @@ pub fn init(boot_info: &'static mut BootInfo) {
if !memory_init {
panic!("memory init failed");
}
device::init(boot_info.framebuffer.as_mut().unwrap());
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
trap::init();
enable_common_cpu_features();
unsafe {
for i in 0..256 {
IRQ_CALLBACK_LIST.push(IrqLine::acquire(i as u8).on_active(general_handler))
}
}
// uncomment below code to enable timer interrupt
// x86_64_util::enable_interrupts_and_hlt();
}
fn general_handler(trap_frame: TrapFrame) {
fn general_handler(trap_frame: &TrapFrame) {
println!("{:#x?}", trap_frame);
println!("rip = 0x{:x}", trap_frame.rip);
println!("rsp = 0x{:x}", trap_frame.rsp);
@ -113,7 +115,7 @@ where
T: Fn(),
{
fn run(&self) {
serial_print!("{}...\t", core::any::type_name::<T>());
serial_print!("{}...\n", core::any::type_name::<T>());
self();
serial_println!("[ok]");
}

View File

@ -1,6 +1,7 @@
//! Timer.
use crate::prelude::*;
use crate::{prelude::*, device::{TimerCallback, TICK,TIMER_FREQ}};
use spin::Mutex;
use core::time::Duration;
/// A timer invokes a callback function after a specified span of time elapsed.
@ -11,33 +12,86 @@ use core::time::Duration;
///
/// Timers are one-shot. If the time is out, one has to set the timer again
/// in order to trigger the callback again.
pub struct Timer {}
pub struct Timer {
function: Arc<dyn Fn(Arc<Self>)+Send+Sync>,
inner: Mutex<TimerInner>,
}
#[derive(Default)]
struct TimerInner{
start_tick: u64,
timeout_tick:u64,
timer_callback:Option<Arc<TimerCallback>>,
}
fn timer_callback(callback:&TimerCallback){
let data = callback.data();
if data.is::<Arc<Timer>>(){
let timer = data.downcast_ref::<Arc<Timer>>().unwrap();
timer.function.call((timer.clone(),));
}else{
panic!("the timer callback is not Timer structure");
}
}
const NANOS_DIVIDE : u64 = 1_000_000_000/TIMER_FREQ;
impl Timer {
/// Creates a new instance, given a callback function.
pub fn new<F>(f: F) -> Result<Self>
pub fn new<F>(f: F) -> Result<Arc<Self>>
where
F: FnMut(&Self),
F: Fn(Arc<Timer>) +Send+Sync+'static,
{
todo!()
Ok(Arc::new(Self { function: Arc::new(f),inner:Mutex::new(TimerInner::default()) }))
}
/// Set a timeout value.
///
/// If a timeout value is already set, the timeout value will be refreshed.
pub fn set(&self, timeout: Duration) {
todo!()
///
pub fn set(self : Arc<Self>, timeout: Duration) {
let mut lock = self.inner.lock();
match &lock.timer_callback{
Some(callback) => {
callback.disable();
},
None => {},
}
let tick_count = timeout.as_secs()*TIMER_FREQ
+ if timeout.subsec_nanos() !=0{(timeout.subsec_nanos() as u64 - 1)/NANOS_DIVIDE + 1} else {0};
unsafe{
lock.start_tick = TICK;
lock.timeout_tick = TICK+tick_count;
}
lock.timer_callback = Some(crate::device::add_timeout_list(tick_count, self.clone(), timer_callback));
}
/// Returns the remaining timeout value.
///
/// If the timer is not set, then the remaining timeout value is zero.
pub fn remain(&self) -> Duration {
todo!()
let lock = self.inner.lock();
let tick_remain;
unsafe{
tick_remain = lock.timeout_tick as i64-TICK as i64;
}
if tick_remain<=0{
Duration::new(0,0)
}else{
let second_count = tick_remain as u64/TIMER_FREQ;
let remain_count = tick_remain as u64 % TIMER_FREQ;
Duration::new(second_count,(remain_count * NANOS_DIVIDE) as u32)
}
}
/// Clear the timeout value.
pub fn clear(&self) {
todo!()
let mut lock = self.inner.lock();
if let Some(callback) = &lock.timer_callback{
callback.disable();
}
lock.timeout_tick = 0;
lock.start_tick = 0;
lock.timer_callback = None;
}
}

View File

@ -5,7 +5,7 @@ use crate::task::{
use super::{irq::IRQ_LIST, *};
#[no_mangle]
pub(crate) extern "C" fn syscall_handler(f: &'static mut SyscallFrame) -> isize {
pub(crate) extern "C" fn syscall_handler(f: &mut SyscallFrame) -> isize {
let r = &f.caller;
let current = Task::current();
current.inner_exclusive_access().is_from_trap = false;
@ -20,7 +20,7 @@ pub(crate) extern "C" fn syscall_handler(f: &'static mut SyscallFrame) -> isize
}
#[no_mangle]
pub(crate) extern "C" fn trap_handler(f: &'static mut TrapFrame) {
pub(crate) extern "C" fn trap_handler(f: &mut TrapFrame) {
if !is_from_kernel(f.cs) {
let current = Task::current();
current.inner_exclusive_access().is_from_trap = true;
@ -37,11 +37,18 @@ pub(crate) extern "C" fn trap_handler(f: &'static mut TrapFrame) {
let irq_line = IRQ_LIST.get(f.id as usize).unwrap();
let callback_functions = irq_line.callback_list();
for callback_function in callback_functions.iter() {
callback_function.call(f.clone());
callback_function.call(f);
}
}
} else {
panic!("cannot handle kernel exception now");
if is_cpu_fault(f){
panic!("cannot handle kernel cpu fault now");
}
let irq_line = IRQ_LIST.get(f.id as usize).unwrap();
let callback_functions = irq_line.callback_list();
for callback_function in callback_functions.iter() {
callback_function.call(f);
}
}
}

View File

@ -20,6 +20,14 @@ pub fn allocate_irq() -> Result<IrqAllocateHandle> {
}
}
pub(crate) fn allocate_target_irq(target_irq: u8) -> Result<IrqAllocateHandle> {
if NOT_USING_IRQ.lock().get_target(target_irq as usize) {
Ok(IrqAllocateHandle::new(target_irq))
} else {
Err(Error::NotEnoughResources)
}
}
/// The handle to a allocate irq number between [32,256), used in std and other parts in kxos
///
/// When the handle is dropped, all the callback in this will be unregistered automatically.
@ -50,7 +58,7 @@ impl IrqAllocateHandle {
/// For each IRQ line, multiple callbacks may be registered.
pub fn on_active<F>(&mut self, callback: F)
where
F: Fn(TrapFrame) + Sync + Send + 'static,
F: Fn(&TrapFrame) + Sync + Send + 'static,
{
self.callbacks.push(self.irq.on_active(callback))
}
@ -87,12 +95,12 @@ lazy_static! {
}
pub struct CallbackElement {
function: Box<dyn Fn(TrapFrame) + Send + Sync + 'static>,
function: Box<dyn Fn(&TrapFrame) + Send + Sync + 'static>,
id: usize,
}
impl CallbackElement {
pub fn call(&self, element: TrapFrame) {
pub fn call(&self, element: &TrapFrame) {
self.function.call((element,));
}
}
@ -140,7 +148,7 @@ impl IrqLine {
/// For each IRQ line, multiple callbacks may be registered.
pub fn on_active<F>(&self, callback: F) -> IrqCallbackHandle
where
F: Fn(TrapFrame) + Sync + Send + 'static,
F: Fn(&TrapFrame) + Sync + Send + 'static,
{
let allocate_id = ID_ALLOCATOR.lock().alloc();
self.callback_list.lock().push(CallbackElement {

View File

@ -2,7 +2,7 @@ mod handler;
mod irq;
pub use self::irq::{allocate_irq, IrqAllocateHandle};
pub(crate) use self::irq::{IrqCallbackHandle, IrqLine};
pub(crate) use self::irq::{allocate_target_irq, IrqCallbackHandle, IrqLine};
use core::{fmt::Debug, mem::size_of_val};
use crate::{x86_64_util::*, *};

View File

@ -3,6 +3,7 @@ use alloc::vec::Vec;
pub struct RecycleAllocator {
current: usize,
recycled: Vec<usize>,
skip: Vec<usize>,
max: usize,
}
@ -11,6 +12,7 @@ impl RecycleAllocator {
RecycleAllocator {
current: 0,
recycled: Vec::new(),
skip: Vec::new(),
max: usize::MAX - 1,
}
}
@ -19,30 +21,73 @@ impl RecycleAllocator {
RecycleAllocator {
current: start,
recycled: Vec::new(),
skip: Vec::new(),
max: max,
}
}
#[allow(unused)]
pub fn alloc(&mut self) -> usize {
if self.current == self.max && self.recycled.is_empty() {
if let Some(id) = self.recycled.pop() {
return id;
}
// recycle list is empty, need to use current to allocate an id.
// it should skip the element in skip list
while self.skip.contains(&self.current) {
self.current += 1;
}
if self.current == self.max {
return usize::MAX;
}
if let Some(id) = self.recycled.pop() {
id
} else {
self.current += 1;
self.current - 1
}
self.current += 1;
self.current - 1
}
/// deallocate a id, it should fit one of the following requirement, otherwise it will panic:
///
/// 1. It is in the skip list
///
/// 2. It smaller than current and not in recycled list
#[allow(unused)]
pub fn dealloc(&mut self, id: usize) {
assert!(id < self.current);
assert!(
!self.recycled.iter().any(|i| *i == id),
"id {} has been deallocated!",
id
);
if !self.skip.contains(&id) {
assert!(id < self.current);
assert!(
!self.recycled.iter().any(|i| *i == id),
"id {} has been deallocated!",
id
);
} else {
// if the value is in skip list, then remove it from the skip list
self.skip.retain(|value| *value != id);
}
self.recycled.push(id);
}
/// get target id in the list, it will return true if the target can used, false if can not used.
/// the target need to meet one of the following requirement so that it can used:
///
/// 1. It is in the recycled list
///
/// 2. It is bigger than the current, smaller than max and not in the skip list
///
pub fn get_target(&mut self, target: usize) -> bool {
if target >= self.max {
return false;
}
if target >= self.current {
if self.skip.contains(&target) {
false
} else {
self.skip.push(target);
true
}
} else {
if self.recycled.contains(&target) {
self.recycled.retain(|value| *value != target);
true
} else {
false
}
}
}
}

View File

@ -77,6 +77,13 @@ pub fn enable_interrupts_and_hlt() {
}
}
#[inline]
pub fn enable_interrupts() {
unsafe {
asm!("sti", options(nomem, nostack));
}
}
pub const RING0: u16 = 0;
pub const RING3: u16 = 3;

View File

@ -124,7 +124,7 @@ impl BlockDevice for VirtioBlockDevice {
impl VirtioBlockDevice {
fn new(mut virtio_device: PCIVirtioDevice) -> Self {
fn handle_block_device(frame: TrapFrame) {
fn handle_block_device(frame: &TrapFrame) {
info!("pci block device queue interrupt");
BLOCK_DEVICE.lock().as_ref().unwrap().handle_irq()
}

View File

@ -272,7 +272,7 @@ impl PCIVirtioDevice {
/// register the queue interrupt functions, this function should call only once
pub fn register_queue_interrupt_functions<F>(&mut self, functions: &mut Vec<F>)
where
F: Fn(TrapFrame) + Send + Sync + 'static,
F: Fn(&TrapFrame) + Send + Sync + 'static,
{
let len = functions.len();
if len

46
src/tests/timer_test.rs Normal file
View File

@ -0,0 +1,46 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(kxos_frame::test_runner)]
#![reexport_test_harness_main = "test_main"]
use bootloader::{entry_point, BootInfo};
use kxos_frame::timer::Timer;
extern crate alloc;
use alloc::sync::Arc;
use core::time::Duration;
use core::panic::PanicInfo;
use kxos_frame::println;
static mut TICK: usize = 0;
entry_point!(kernel_test_main);
fn kernel_test_main(boot_info: &'static mut BootInfo) -> ! {
kxos_frame::init(boot_info);
test_main();
loop {}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
kxos_frame::test_panic_handler(info)
}
#[test_case]
fn test_timer() {
println!("If you want to pass this test, you may need to enable the interrupt in kxos_frame/lib.rs");
println!("make sure the Timer irq number 32 handler won't panic");
unsafe {
let timer = Timer::new(timer_callback).unwrap();
timer.set(Duration::from_secs(1));
while TICK < 5 {}
}
}
pub fn timer_callback(timer: Arc<Timer>) {
unsafe {
TICK+=1;
println!("TICK:{}",TICK);
timer.set(Duration::from_secs(1));
}
}