From 280591db666eb980e93c67d43f8b248ec1f8b75e Mon Sep 17 00:00:00 2001 From: sdww0 <1315953661@qq.com> Date: Wed, 9 Nov 2022 20:33:41 +0800 Subject: [PATCH] finish PIT Timer and add testcase --- src/kxos-frame/src/device/mod.rs | 4 + src/kxos-frame/src/device/pic.rs | 182 +++++++++++++++++++ src/kxos-frame/src/lib.rs | 14 +- src/kxos-frame/src/timer.rs | 72 +++++++- src/kxos-frame/src/trap/handler.rs | 15 +- src/kxos-frame/src/trap/irq.rs | 16 +- src/kxos-frame/src/trap/mod.rs | 2 +- src/kxos-frame/src/util/recycle_allocator.rs | 71 ++++++-- src/kxos-frame/src/x86_64_util.rs | 7 + src/kxos-std/src/driver/pci/virtio/block.rs | 2 +- src/kxos-virtio/src/lib.rs | 2 +- src/tests/timer_test.rs | 46 +++++ 12 files changed, 394 insertions(+), 39 deletions(-) create mode 100644 src/kxos-frame/src/device/pic.rs create mode 100644 src/tests/timer_test.rs diff --git a/src/kxos-frame/src/device/mod.rs b/src/kxos-frame/src/device/mod.rs index eff48e93..e0337b7b 100644 --- a/src/kxos-frame/src/device/mod.rs +++ b/src/kxos-frame/src/device/mod.rs @@ -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(); } diff --git a/src/kxos-frame/src/device/pic.rs b/src/kxos-frame/src/device/pic.rs new file mode 100644 index 00000000..c55282cb --- /dev/null +++ b/src/kxos-frame/src/device/pic.rs @@ -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 = 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>= 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>> = Cell::new(BinaryHeap::new()) ; +} + +pub struct TimerCallback { + expire_ms: u64, + data: Arc, + callback: Box, + enable: Cell, +} + +impl TimerCallback { + fn new( + timeout_ms: u64, + data: Arc, + callback: Box, + ) -> Self { + Self { + expire_ms: timeout_ms, + data, + callback, + enable:Cell::new(true), + } + } + + pub fn data(&self) -> &Arc { + &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 { + 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(timeout: u64, data: T, callback: F) -> Arc +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 + } +} diff --git a/src/kxos-frame/src/lib.rs b/src/kxos-frame/src/lib.rs index be481f24..13c44156 100644 --- a/src/kxos-frame/src/lib.rs +++ b/src/kxos-frame/src/lib.rs @@ -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::()); + serial_print!("{}...\n", core::any::type_name::()); self(); serial_println!("[ok]"); } diff --git a/src/kxos-frame/src/timer.rs b/src/kxos-frame/src/timer.rs index 9fa3d499..ba9a3820 100644 --- a/src/kxos-frame/src/timer.rs +++ b/src/kxos-frame/src/timer.rs @@ -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)+Send+Sync>, + inner: Mutex, +} +#[derive(Default)] +struct TimerInner{ + start_tick: u64, + timeout_tick:u64, + timer_callback:Option>, +} + +fn timer_callback(callback:&TimerCallback){ + let data = callback.data(); + if data.is::>(){ + let timer = data.downcast_ref::>().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) -> Result + pub fn new(f: F) -> Result> where - F: FnMut(&Self), + F: Fn(Arc) +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, 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; } } diff --git a/src/kxos-frame/src/trap/handler.rs b/src/kxos-frame/src/trap/handler.rs index c6fbf5cb..b3d4cabf 100644 --- a/src/kxos-frame/src/trap/handler.rs +++ b/src/kxos-frame/src/trap/handler.rs @@ -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); + } } } diff --git a/src/kxos-frame/src/trap/irq.rs b/src/kxos-frame/src/trap/irq.rs index 112f744f..9c9ca371 100644 --- a/src/kxos-frame/src/trap/irq.rs +++ b/src/kxos-frame/src/trap/irq.rs @@ -20,6 +20,14 @@ pub fn allocate_irq() -> Result { } } +pub(crate) fn allocate_target_irq(target_irq: u8) -> Result { + 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(&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, + function: Box, 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(&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 { diff --git a/src/kxos-frame/src/trap/mod.rs b/src/kxos-frame/src/trap/mod.rs index 453cb151..5ae6dc5b 100644 --- a/src/kxos-frame/src/trap/mod.rs +++ b/src/kxos-frame/src/trap/mod.rs @@ -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::*, *}; diff --git a/src/kxos-frame/src/util/recycle_allocator.rs b/src/kxos-frame/src/util/recycle_allocator.rs index 04f1c8da..8d1932bd 100644 --- a/src/kxos-frame/src/util/recycle_allocator.rs +++ b/src/kxos-frame/src/util/recycle_allocator.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; pub struct RecycleAllocator { current: usize, recycled: Vec, + skip: Vec, 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 + } + } + } } diff --git a/src/kxos-frame/src/x86_64_util.rs b/src/kxos-frame/src/x86_64_util.rs index 10c8d05a..73233019 100644 --- a/src/kxos-frame/src/x86_64_util.rs +++ b/src/kxos-frame/src/x86_64_util.rs @@ -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; diff --git a/src/kxos-std/src/driver/pci/virtio/block.rs b/src/kxos-std/src/driver/pci/virtio/block.rs index 62d6e342..785d9da7 100644 --- a/src/kxos-std/src/driver/pci/virtio/block.rs +++ b/src/kxos-std/src/driver/pci/virtio/block.rs @@ -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() } diff --git a/src/kxos-virtio/src/lib.rs b/src/kxos-virtio/src/lib.rs index 692fa662..03d48517 100644 --- a/src/kxos-virtio/src/lib.rs +++ b/src/kxos-virtio/src/lib.rs @@ -272,7 +272,7 @@ impl PCIVirtioDevice { /// register the queue interrupt functions, this function should call only once pub fn register_queue_interrupt_functions(&mut self, functions: &mut Vec) where - F: Fn(TrapFrame) + Send + Sync + 'static, + F: Fn(&TrapFrame) + Send + Sync + 'static, { let len = functions.len(); if len diff --git a/src/tests/timer_test.rs b/src/tests/timer_test.rs new file mode 100644 index 00000000..7d9cad0a --- /dev/null +++ b/src/tests/timer_test.rs @@ -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) { + unsafe { + TICK+=1; + println!("TICK:{}",TICK); + timer.set(Duration::from_secs(1)); + } +}