mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 07:06:47 +00:00
添加rust重构版本的HPET驱动和tsc驱动,并使用HPET校准tsc频率和cpu总线频率 (#412)
* 添加rust重构版本的HPET驱动和tsc驱动,并使用HPET校准tsc频率和cpu总线频率 * 把hpet.c移动到arch文件夹下
This commit is contained in:
parent
ad1d649edd
commit
fbe6becd6d
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -172,7 +172,8 @@
|
|||||||
"cmd_test.h": "c",
|
"cmd_test.h": "c",
|
||||||
"cmpxchg.h": "c",
|
"cmpxchg.h": "c",
|
||||||
"mman.h": "c",
|
"mman.h": "c",
|
||||||
"clocksource.h": "c"
|
"clocksource.h": "c",
|
||||||
|
"ata.h": "c"
|
||||||
},
|
},
|
||||||
"C_Cpp.errorSquiggles": "enabled",
|
"C_Cpp.errorSquiggles": "enabled",
|
||||||
"esbonio.sphinx.confDir": "",
|
"esbonio.sphinx.confDir": "",
|
||||||
|
@ -40,7 +40,7 @@ ida = { path = "src/libs/ida" }
|
|||||||
# 构建时依赖项
|
# 构建时依赖项
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "0.61.0"
|
bindgen = "0.61.0"
|
||||||
|
cc = { version = "1.0.83", features = ["parallel"] }
|
||||||
|
|
||||||
[dependencies.lazy_static]
|
[dependencies.lazy_static]
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
extern crate bindgen;
|
extern crate bindgen;
|
||||||
|
extern crate cc;
|
||||||
// use ::std::env;
|
// use ::std::env;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use cc::Build;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Tell cargo to look for shared libraries in the specified directory
|
// Tell cargo to look for shared libraries in the specified directory
|
||||||
println!("cargo:rustc-link-search=src");
|
println!("cargo:rustc-link-search=src");
|
||||||
println!("cargo:rerun-if-changed=src/include/bindings/wrapper.h");
|
println!("cargo:rerun-if-changed=src/include/bindings/wrapper.h");
|
||||||
|
|
||||||
|
generate_bindings();
|
||||||
|
CFilesBuilder::build();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_bindings() {
|
||||||
// let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
// let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
let out_path = PathBuf::from(String::from("src/include/bindings/"));
|
let out_path = PathBuf::from(String::from("src/include/bindings/"));
|
||||||
|
|
||||||
@ -45,3 +53,58 @@ fn main() {
|
|||||||
.expect("Couldn't write bindings!");
|
.expect("Couldn't write bindings!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 构建项目的c文件
|
||||||
|
struct CFilesBuilder;
|
||||||
|
|
||||||
|
impl CFilesBuilder {
|
||||||
|
fn build() {
|
||||||
|
let mut c = cc::Build::new();
|
||||||
|
Self::setup_global_flags(&mut c);
|
||||||
|
Self::setup_defines(&mut c);
|
||||||
|
Self::setup_global_include_dir(&mut c);
|
||||||
|
Self::setup_files(&mut c);
|
||||||
|
c.compile("dragonos_kernel_cfiles");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_global_flags(c: &mut Build) {
|
||||||
|
c.flag("-mcmodel=large")
|
||||||
|
.flag("-fno-builtin")
|
||||||
|
.flag("-nostdlib")
|
||||||
|
.flag("-fno-stack-protector")
|
||||||
|
.flag("-fno-pie")
|
||||||
|
.flag("-Wno-expansion-to-defined")
|
||||||
|
.flag("-Wno-unused-parameter")
|
||||||
|
.flag("-m64")
|
||||||
|
.flag("-O1");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_defines(c: &mut Build) {
|
||||||
|
if let Ok(k) = std::env::var("EMULATOR") {
|
||||||
|
c.define("EMULATOR", Some(k.as_str()));
|
||||||
|
} else {
|
||||||
|
c.define("EMULATOR", "__NO_EMULATION__");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
c.define("__x86_64__", None);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.define("PIC", "_INTR_APIC_");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_global_include_dir(c: &mut Build) {
|
||||||
|
c.include("src/include");
|
||||||
|
c.include("src");
|
||||||
|
c.include(".");
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
c.include("src/arch/x86_64/include");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置需要编译的文件
|
||||||
|
fn setup_files(c: &mut Build) {
|
||||||
|
c.file("src/arch/x86_64/driver/hpet.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use super::setup::setup_arch;
|
use crate::time::TimeArch;
|
||||||
|
|
||||||
|
use super::{driver::tsc::TSCManager, setup::setup_arch, CurrentTimeArch};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn rs_setup_arch() -> i32 {
|
unsafe extern "C" fn rs_setup_arch() -> i32 {
|
||||||
@ -6,3 +8,14 @@ unsafe extern "C" fn rs_setup_arch() -> i32 {
|
|||||||
.map(|_| 0)
|
.map(|_| 0)
|
||||||
.unwrap_or_else(|e| e.to_posix_errno());
|
.unwrap_or_else(|e| e.to_posix_errno());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取当前的时间戳
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_get_cycles() -> u64 {
|
||||||
|
return CurrentTimeArch::get_cycles() as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_tsc_get_cpu_khz() -> u64 {
|
||||||
|
return TSCManager::cpu_khz();
|
||||||
|
}
|
||||||
|
31
kernel/src/arch/x86_64/driver/c_adapter.rs
Normal file
31
kernel/src/arch/x86_64/driver/c_adapter.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use super::{
|
||||||
|
hpet::{hpet_init, hpet_instance},
|
||||||
|
tsc::TSCManager,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_hpet_init() -> i32 {
|
||||||
|
hpet_init()
|
||||||
|
.map(|_| 0)
|
||||||
|
.unwrap_or_else(|e| e.to_posix_errno())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_hpet_enable() -> i32 {
|
||||||
|
hpet_instance()
|
||||||
|
.hpet_enable()
|
||||||
|
.map(|_| 0)
|
||||||
|
.unwrap_or_else(|e| e.to_posix_errno())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_tsc_init() -> i32 {
|
||||||
|
TSCManager::init()
|
||||||
|
.map(|_| 0)
|
||||||
|
.unwrap_or_else(|e| e.to_posix_errno())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn rs_handle_hpet_irq(timer_num: u32) {
|
||||||
|
hpet_instance().handle_irq(timer_num);
|
||||||
|
}
|
26
kernel/src/arch/x86_64/driver/hpet.c
Normal file
26
kernel/src/arch/x86_64/driver/hpet.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include <common/glib.h>
|
||||||
|
#include <common/kprint.h>
|
||||||
|
#include <driver/interrupt/apic/apic.h>
|
||||||
|
|
||||||
|
extern void rs_handle_hpet_irq(uint32_t timer_num);
|
||||||
|
|
||||||
|
hardware_intr_controller HPET_intr_controller =
|
||||||
|
{
|
||||||
|
.enable = apic_ioapic_enable,
|
||||||
|
.disable = apic_ioapic_disable,
|
||||||
|
.install = apic_ioapic_install,
|
||||||
|
.uninstall = apic_ioapic_uninstall,
|
||||||
|
.ack = apic_ioapic_edge_ack,
|
||||||
|
};
|
||||||
|
|
||||||
|
void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
rs_handle_hpet_irq(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void c_hpet_register_irq()
|
||||||
|
{
|
||||||
|
struct apic_IO_APIC_RTE_entry entry;
|
||||||
|
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
|
||||||
|
irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
|
||||||
|
}
|
251
kernel/src/arch/x86_64/driver/hpet.rs
Normal file
251
kernel/src/arch/x86_64/driver/hpet.rs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
use core::{
|
||||||
|
ffi::c_void,
|
||||||
|
intrinsics::unlikely,
|
||||||
|
mem::size_of,
|
||||||
|
ptr::NonNull,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use acpi::HpetInfo;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
driver::{
|
||||||
|
acpi::acpi_manager,
|
||||||
|
timers::hpet::{HpetRegisters, HpetTimerRegisters},
|
||||||
|
},
|
||||||
|
exception::softirq::{softirq_vectors, SoftirqNumber},
|
||||||
|
kdebug, kerror, kinfo,
|
||||||
|
libs::{
|
||||||
|
rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||||
|
volatile::volwrite,
|
||||||
|
},
|
||||||
|
mm::{
|
||||||
|
mmio_buddy::{mmio_pool, MMIOSpaceGuard},
|
||||||
|
PhysAddr,
|
||||||
|
},
|
||||||
|
syscall::SystemError,
|
||||||
|
time::timer::{clock, timer_get_first_expire, update_timer_jiffies},
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn c_hpet_register_irq() -> c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut HPET_INSTANCE: Option<Hpet> = None;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn hpet_instance() -> &'static Hpet {
|
||||||
|
unsafe { HPET_INSTANCE.as_ref().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Hpet {
|
||||||
|
info: HpetInfo,
|
||||||
|
_mmio_guard: MMIOSpaceGuard,
|
||||||
|
inner: RwLock<InnerHpet>,
|
||||||
|
enabled: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InnerHpet {
|
||||||
|
registers_ptr: NonNull<HpetRegisters>,
|
||||||
|
timer_registers_ptr: NonNull<HpetTimerRegisters>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hpet {
|
||||||
|
/// HPET0 中断间隔为500us
|
||||||
|
pub const HPET0_INTERVAL_USEC: u64 = 500;
|
||||||
|
|
||||||
|
fn new(mut hpet_info: HpetInfo) -> Result<Self, SystemError> {
|
||||||
|
let paddr = PhysAddr::new(hpet_info.base_address);
|
||||||
|
let map_size = size_of::<HpetRegisters>();
|
||||||
|
let mmio = mmio_pool().create_mmio(map_size)?;
|
||||||
|
unsafe { mmio.map_phys(paddr, map_size)? };
|
||||||
|
let hpet = unsafe {
|
||||||
|
(mmio.vaddr().data() as *const HpetRegisters)
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let tm_num = hpet.timers_num();
|
||||||
|
kinfo!("HPET has {} timers", tm_num);
|
||||||
|
hpet_info.hpet_number = tm_num as u8;
|
||||||
|
drop(hpet);
|
||||||
|
drop(mmio);
|
||||||
|
if tm_num == 0 {
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes_to_map = size_of::<HpetRegisters>()
|
||||||
|
+ hpet_info.hpet_number as usize * size_of::<HpetTimerRegisters>();
|
||||||
|
let mmio = mmio_pool().create_mmio(bytes_to_map)?;
|
||||||
|
|
||||||
|
unsafe { mmio.map_phys(paddr, bytes_to_map)? };
|
||||||
|
let ptr = NonNull::new(mmio.vaddr().data() as *mut HpetRegisters).unwrap();
|
||||||
|
let timer_ptr = NonNull::new(
|
||||||
|
(mmio.vaddr().data() + size_of::<HpetRegisters>()) as *mut HpetTimerRegisters,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let hpet = Hpet {
|
||||||
|
info: hpet_info,
|
||||||
|
_mmio_guard: mmio,
|
||||||
|
inner: RwLock::new(InnerHpet {
|
||||||
|
registers_ptr: ptr,
|
||||||
|
timer_registers_ptr: timer_ptr,
|
||||||
|
}),
|
||||||
|
enabled: AtomicBool::new(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(hpet);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enabled(&self) -> bool {
|
||||||
|
self.enabled.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 使能HPET
|
||||||
|
pub(super) fn hpet_enable(&self) -> Result<(), SystemError> {
|
||||||
|
// !!!这里是临时糊代码的,需要在apic重构的时候修改!!!
|
||||||
|
let (inner_guard, regs) = unsafe { self.hpet_regs_mut() };
|
||||||
|
let freq = regs.frequency();
|
||||||
|
kdebug!("HPET frequency: {} Hz", freq);
|
||||||
|
let ticks = Self::HPET0_INTERVAL_USEC * freq / 1000000;
|
||||||
|
if ticks <= 0 || ticks > freq * 8 {
|
||||||
|
kerror!("HPET enable: ticks '{ticks}' is invalid");
|
||||||
|
return Err(SystemError::EINVAL);
|
||||||
|
}
|
||||||
|
if unlikely(regs.timers_num() == 0) {
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { regs.write_main_counter_value(0) };
|
||||||
|
|
||||||
|
drop(regs);
|
||||||
|
drop(inner_guard);
|
||||||
|
|
||||||
|
let (inner_guard, timer_reg) = unsafe { self.timer_mut(0).ok_or(SystemError::ENODEV) }?;
|
||||||
|
|
||||||
|
let timer_reg = NonNull::new(timer_reg as *mut HpetTimerRegisters).unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// 设置定时器0为周期定时,边沿触发,默认投递到IO APIC的2号引脚(看conf寄存器的高32bit,哪一位被置1,则可以投递到哪一个I/O apic引脚)
|
||||||
|
volwrite!(timer_reg, config, 0x004c);
|
||||||
|
volwrite!(timer_reg, comparator_value, ticks);
|
||||||
|
}
|
||||||
|
drop(timer_reg);
|
||||||
|
drop(inner_guard);
|
||||||
|
|
||||||
|
// todo!("register irq in C");
|
||||||
|
unsafe { c_hpet_register_irq() };
|
||||||
|
self.enabled.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let (inner_guard, regs) = unsafe { self.hpet_regs_mut() };
|
||||||
|
|
||||||
|
// 置位旧设备中断路由兼容标志位、定时器组使能标志位
|
||||||
|
unsafe { regs.write_general_config(3) };
|
||||||
|
|
||||||
|
drop(regs);
|
||||||
|
drop(inner_guard);
|
||||||
|
|
||||||
|
kinfo!("HPET enabled");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner(&self) -> RwLockReadGuard<InnerHpet> {
|
||||||
|
self.inner.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner_mut(&self) -> RwLockWriteGuard<InnerHpet> {
|
||||||
|
self.inner.write()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn timer(&self, index: u8) -> Option<(RwLockReadGuard<InnerHpet>, &HpetTimerRegisters)> {
|
||||||
|
let inner = self.inner();
|
||||||
|
if index >= self.info.hpet_number {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let timer_regs = unsafe {
|
||||||
|
inner
|
||||||
|
.timer_registers_ptr
|
||||||
|
.as_ptr()
|
||||||
|
.add(index as usize)
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
return Some((inner, timer_regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn timer_mut(
|
||||||
|
&self,
|
||||||
|
index: u8,
|
||||||
|
) -> Option<(RwLockWriteGuard<InnerHpet>, &mut HpetTimerRegisters)> {
|
||||||
|
let inner = self.inner_mut();
|
||||||
|
if index >= self.info.hpet_number {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let timer_regs = unsafe {
|
||||||
|
inner
|
||||||
|
.timer_registers_ptr
|
||||||
|
.as_ptr()
|
||||||
|
.add(index as usize)
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
return Some((inner, timer_regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn hpet_regs(&self) -> (RwLockReadGuard<InnerHpet>, &HpetRegisters) {
|
||||||
|
let inner = self.inner();
|
||||||
|
let regs = unsafe { inner.registers_ptr.as_ref() };
|
||||||
|
return (inner, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn hpet_regs_mut(&self) -> (RwLockWriteGuard<InnerHpet>, &mut HpetRegisters) {
|
||||||
|
let mut inner = self.inner_mut();
|
||||||
|
let regs = unsafe { inner.registers_ptr.as_mut() };
|
||||||
|
return (inner, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_counter_value(&self) -> u64 {
|
||||||
|
let (inner_guard, regs) = unsafe { self.hpet_regs() };
|
||||||
|
let value = regs.main_counter_value();
|
||||||
|
drop(regs);
|
||||||
|
drop(inner_guard);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn period(&self) -> u64 {
|
||||||
|
let (inner_guard, regs) = unsafe { self.hpet_regs() };
|
||||||
|
let period = regs.counter_clock_period();
|
||||||
|
kdebug!("HPET period: {}", period);
|
||||||
|
drop(regs);
|
||||||
|
drop(inner_guard);
|
||||||
|
return period;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 处理HPET的中断
|
||||||
|
pub(super) fn handle_irq(&self, timer_num: u32) {
|
||||||
|
if timer_num == 0 {
|
||||||
|
update_timer_jiffies(Self::HPET0_INTERVAL_USEC);
|
||||||
|
|
||||||
|
if let Ok(first_expire) = timer_get_first_expire() {
|
||||||
|
if first_expire <= clock() {
|
||||||
|
softirq_vectors().raise_softirq(SoftirqNumber::TIMER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hpet_init() -> Result<(), SystemError> {
|
||||||
|
let hpet_info = HpetInfo::new(acpi_manager().tables().unwrap()).map_err(|e| {
|
||||||
|
kerror!("Failed to get HPET info: {:?}", e);
|
||||||
|
SystemError::ENODEV
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let hpet_instance = Hpet::new(hpet_info)?;
|
||||||
|
unsafe {
|
||||||
|
HPET_INSTANCE = Some(hpet_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
3
kernel/src/arch/x86_64/driver/mod.rs
Normal file
3
kernel/src/arch/x86_64/driver/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod c_adapter;
|
||||||
|
pub mod hpet;
|
||||||
|
pub mod tsc;
|
384
kernel/src/arch/x86_64/driver/tsc.rs
Normal file
384
kernel/src/arch/x86_64/driver/tsc.rs
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
use core::{
|
||||||
|
cmp::{max, min},
|
||||||
|
intrinsics::unlikely,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arch::{io::PortIOArch, CurrentIrqArch, CurrentPortIOArch, CurrentTimeArch},
|
||||||
|
driver::acpi::pmtmr::{ACPI_PM_OVERRUN, PMTMR_TICKS_PER_SEC},
|
||||||
|
exception::InterruptArch,
|
||||||
|
kdebug, kerror, kinfo, kwarn,
|
||||||
|
syscall::SystemError,
|
||||||
|
time::TimeArch,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::hpet::hpet_instance;
|
||||||
|
|
||||||
|
/// The clock frequency of the i8253/i8254 PIT
|
||||||
|
const PIT_TICK_RATE: u64 = 1193182;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TSCManager;
|
||||||
|
|
||||||
|
static mut TSC_KHZ: u64 = 0;
|
||||||
|
static mut CPU_KHZ: u64 = 0;
|
||||||
|
|
||||||
|
impl TSCManager {
|
||||||
|
const DEFAULT_THRESHOLD: u64 = 0x20000;
|
||||||
|
|
||||||
|
/// 初始化TSC
|
||||||
|
///
|
||||||
|
/// 目前由于未支持acpi pm timer, 因此调用该函数时,HPET应当完成初始化,否则将无法校准TSC
|
||||||
|
///
|
||||||
|
/// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1511
|
||||||
|
pub fn init() -> Result<(), SystemError> {
|
||||||
|
let cpuid = x86::cpuid::CpuId::new();
|
||||||
|
let feat = cpuid.get_feature_info().ok_or(SystemError::ENODEV)?;
|
||||||
|
if !feat.has_tsc() {
|
||||||
|
kerror!("TSC is not available");
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if unsafe { TSC_KHZ == 0 } {
|
||||||
|
if let Err(e) = Self::determine_cpu_tsc_frequency(false) {
|
||||||
|
kerror!("Failed to determine CPU TSC frequency: {:?}", e);
|
||||||
|
// todo: mark TSC as unstable clock source
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: register TSC as clock source and deal with unstable clock source
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取TSC和CPU总线的频率
|
||||||
|
///
|
||||||
|
/// ## 参数
|
||||||
|
///
|
||||||
|
/// - `early`:是否在早期初始化
|
||||||
|
///
|
||||||
|
/// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1438
|
||||||
|
fn determine_cpu_tsc_frequency(early: bool) -> Result<(), SystemError> {
|
||||||
|
if unlikely(Self::cpu_khz() != 0 || Self::tsc_khz() != 0) {
|
||||||
|
kwarn!("TSC and CPU frequency already determined");
|
||||||
|
}
|
||||||
|
|
||||||
|
if early {
|
||||||
|
// todo: 先根据cpuid或者读取msr或者pit来测量TSC和CPU总线的频率
|
||||||
|
todo!("detect TSC and CPU frequency by cpuid or msr or pit");
|
||||||
|
} else {
|
||||||
|
// 使用pit来测量TSC和CPU总线的频率
|
||||||
|
Self::set_cpu_khz(Self::calibrate_cpu_by_pit_hpet_ptimer()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 认为非0的TSC频率是可靠的,并且使用它来检查CPU总线的频率
|
||||||
|
if Self::tsc_khz() == 0 {
|
||||||
|
Self::set_tsc_khz(Self::cpu_khz());
|
||||||
|
} else if (Self::cpu_khz() as i64 - Self::tsc_khz() as i64).abs() * 10
|
||||||
|
> Self::cpu_khz() as i64
|
||||||
|
{
|
||||||
|
// 如果TSC和CPU总线的频率相差太大,那么认为CPU总线的频率是不可靠的,使用TSC的频率
|
||||||
|
Self::set_cpu_khz(Self::tsc_khz());
|
||||||
|
}
|
||||||
|
|
||||||
|
if Self::cpu_khz() == 0 {
|
||||||
|
kerror!("Failed to determine CPU frequency");
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
kinfo!(
|
||||||
|
"Detected {}.{} MHz processor",
|
||||||
|
Self::cpu_khz() / 1000,
|
||||||
|
Self::cpu_khz() % 1000
|
||||||
|
);
|
||||||
|
kinfo!(
|
||||||
|
"Detected {}.{} MHz TSC",
|
||||||
|
Self::tsc_khz() / 1000,
|
||||||
|
Self::tsc_khz() % 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 测量CPU总线的频率
|
||||||
|
///
|
||||||
|
/// 使用pit、hpet、ptimer来测量CPU总线的频率
|
||||||
|
fn calibrate_cpu_by_pit_hpet_ptimer() -> Result<u64, SystemError> {
|
||||||
|
let hpet = hpet_instance().enabled();
|
||||||
|
kdebug!(
|
||||||
|
"Calibrating TSC with {}",
|
||||||
|
if hpet { "HPET" } else { "PMTIMER" }
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut tsc_pit_min = u64::MAX;
|
||||||
|
let mut tsc_ref_min = u64::MAX;
|
||||||
|
|
||||||
|
// 默认的校准参数
|
||||||
|
let cal_ms = 10;
|
||||||
|
let cal_latch = PIT_TICK_RATE / (1000 / cal_ms);
|
||||||
|
let cal_pit_loops = 1000;
|
||||||
|
|
||||||
|
// 如果第一轮校准失败,那么使用这些参数(因为虚拟化平台的问题,第一轮校准可能失败)
|
||||||
|
let cal2_ms = 50;
|
||||||
|
let cal2_latch = PIT_TICK_RATE / (1000 / cal2_ms);
|
||||||
|
let cal2_pit_loops = 5000;
|
||||||
|
|
||||||
|
let mut latch = cal_latch;
|
||||||
|
let mut loopmin = cal_pit_loops;
|
||||||
|
let mut ms = cal_ms;
|
||||||
|
|
||||||
|
let mut global_ref1 = 0;
|
||||||
|
let mut global_ref2 = 0;
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
|
||||||
|
|
||||||
|
let (tsc1, ref1) = Self::read_refs(hpet);
|
||||||
|
let tsc_pit_khz = Self::pit_calibrate_tsc(latch, ms, loopmin).unwrap_or(u64::MAX);
|
||||||
|
let (tsc2, ref2) = Self::read_refs(hpet);
|
||||||
|
drop(irq_guard);
|
||||||
|
|
||||||
|
global_ref1 = ref1;
|
||||||
|
global_ref2 = ref2;
|
||||||
|
|
||||||
|
// 选用最小的tsc_pit_khz
|
||||||
|
tsc_pit_min = min(tsc_pit_min, tsc_pit_khz);
|
||||||
|
|
||||||
|
// HPET或者PTIMER可能是不可用的
|
||||||
|
if ref1 == ref2 {
|
||||||
|
kdebug!("HPET/PMTIMER not available");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查采样是否被打断
|
||||||
|
if tsc1 == u64::MAX || tsc2 == u64::MAX {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tsc2 = (tsc2 - tsc1) * 1000000;
|
||||||
|
|
||||||
|
if hpet {
|
||||||
|
tsc2 = Self::calc_hpet_ref(tsc2, ref1, ref2);
|
||||||
|
} else {
|
||||||
|
tsc2 = Self::calc_pmtimer_ref(tsc2, ref1, ref2);
|
||||||
|
}
|
||||||
|
|
||||||
|
tsc_ref_min = min(tsc_ref_min, tsc2);
|
||||||
|
|
||||||
|
// 检查与参考值的误差
|
||||||
|
let mut delta = tsc_pit_min * 100;
|
||||||
|
delta /= tsc_ref_min;
|
||||||
|
|
||||||
|
// 如果误差在10%以内,那么认为测量成功
|
||||||
|
// 返回参考值,因为它是更精确的
|
||||||
|
if delta >= 90 && delta <= 110 {
|
||||||
|
kinfo!(
|
||||||
|
"PIT calibration matches {}. {} loops",
|
||||||
|
if hpet { "HPET" } else { "PMTIMER" },
|
||||||
|
i + 1
|
||||||
|
);
|
||||||
|
return Ok(tsc_ref_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 1 && tsc_pit_min == u64::MAX {
|
||||||
|
latch = cal2_latch;
|
||||||
|
ms = cal2_ms;
|
||||||
|
loopmin = cal2_pit_loops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tsc_pit_min == u64::MAX {
|
||||||
|
kwarn!("Unable to calibrate against PIT");
|
||||||
|
|
||||||
|
// 如果没有参考值,那么禁用tsc
|
||||||
|
if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) {
|
||||||
|
kwarn!("No reference (HPET/PMTIMER) available");
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tsc_ref_min == u64::MAX {
|
||||||
|
kwarn!("Unable to calibrate against HPET/PMTIMER");
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
kinfo!(
|
||||||
|
"Using {} reference calibration",
|
||||||
|
if hpet { "HPET" } else { "PMTIMER" }
|
||||||
|
);
|
||||||
|
return Ok(tsc_ref_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have an alternative source, use the PIT calibration value
|
||||||
|
if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) {
|
||||||
|
kinfo!("Using PIT calibration value");
|
||||||
|
return Ok(tsc_pit_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The alternative source failed, use the PIT calibration value
|
||||||
|
if tsc_ref_min == u64::MAX {
|
||||||
|
kwarn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value");
|
||||||
|
return Ok(tsc_pit_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The calibration values differ too much. In doubt, we use
|
||||||
|
// the PIT value as we know that there are PMTIMERs around
|
||||||
|
// running at double speed. At least we let the user know:
|
||||||
|
kwarn!(
|
||||||
|
"PIT calibration deviates from {}: tsc_pit_min={}, tsc_ref_min={}",
|
||||||
|
if hpet { "HPET" } else { "PMTIMER" },
|
||||||
|
tsc_pit_min,
|
||||||
|
tsc_ref_min
|
||||||
|
);
|
||||||
|
|
||||||
|
kinfo!("Using PIT calibration value");
|
||||||
|
return Ok(tsc_pit_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 尝试使用PIT来校准tsc时间,并且返回tsc的频率(khz)。
|
||||||
|
/// 如果失败,那么返回None
|
||||||
|
///
|
||||||
|
/// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#389
|
||||||
|
fn pit_calibrate_tsc(latch: u64, ms: u64, loopmin: u64) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
// Set the Gate high, disable speaker
|
||||||
|
let d = (CurrentPortIOArch::in8(0x61) & (!0x02)) | 0x01;
|
||||||
|
CurrentPortIOArch::out8(0x61, d);
|
||||||
|
|
||||||
|
// Setup CTC channel 2* for mode 0, (interrupt on terminal
|
||||||
|
// count mode), binary count. Set the latch register to 50ms
|
||||||
|
// (LSB then MSB) to begin countdown.
|
||||||
|
CurrentPortIOArch::out8(0x43, 0xb0);
|
||||||
|
CurrentPortIOArch::out8(0x42, (latch & 0xff) as u8);
|
||||||
|
CurrentPortIOArch::out8(0x42, ((latch >> 8) & 0xff) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tsc = CurrentTimeArch::get_cycles() as u64;
|
||||||
|
let t1 = tsc;
|
||||||
|
let mut t2 = tsc;
|
||||||
|
let mut pitcnt = 0u64;
|
||||||
|
let mut tscmax = 0u64;
|
||||||
|
let mut tscmin = u64::MAX;
|
||||||
|
while unsafe { (CurrentPortIOArch::in8(0x61) & 0x20) == 0 } {
|
||||||
|
t2 = CurrentTimeArch::get_cycles() as u64;
|
||||||
|
let delta = t2 - tsc;
|
||||||
|
tsc = t2;
|
||||||
|
|
||||||
|
tscmin = min(tscmin, delta);
|
||||||
|
tscmax = max(tscmax, delta);
|
||||||
|
|
||||||
|
pitcnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks:
|
||||||
|
//
|
||||||
|
// If we were not able to read the PIT more than loopmin
|
||||||
|
// times, then we have been hit by a massive SMI
|
||||||
|
//
|
||||||
|
// If the maximum is 10 times larger than the minimum,
|
||||||
|
// then we got hit by an SMI as well.
|
||||||
|
if pitcnt < loopmin || tscmax > 10 * tscmin {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut delta = t2 - t1;
|
||||||
|
delta /= ms;
|
||||||
|
|
||||||
|
return Some(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 读取tsc和参考值
|
||||||
|
///
|
||||||
|
/// ## 参数
|
||||||
|
///
|
||||||
|
/// - `hpet_enabled`:是否启用hpet
|
||||||
|
///
|
||||||
|
/// ## 返回
|
||||||
|
///
|
||||||
|
/// - `Ok((tsc, ref))`:tsc和参考值
|
||||||
|
///
|
||||||
|
/// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#317
|
||||||
|
fn read_refs(hpet_enabled: bool) -> (u64, u64) {
|
||||||
|
let thresh = if Self::tsc_khz() == 0 {
|
||||||
|
Self::DEFAULT_THRESHOLD
|
||||||
|
} else {
|
||||||
|
Self::tsc_khz() >> 5
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ref_ret = 0;
|
||||||
|
for _ in 0..5 {
|
||||||
|
let t1 = CurrentTimeArch::get_cycles() as u64;
|
||||||
|
if hpet_enabled {
|
||||||
|
ref_ret = hpet_instance().main_counter_value();
|
||||||
|
} else {
|
||||||
|
todo!("read pmtimer")
|
||||||
|
}
|
||||||
|
let t2 = CurrentTimeArch::get_cycles() as u64;
|
||||||
|
if (t2 - t1) < thresh {
|
||||||
|
return (t2, ref_ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kwarn!("TSCManager: Failed to read reference value, tsc delta too high");
|
||||||
|
return (u64::MAX, ref_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据HPET的参考值计算tsc的频率
|
||||||
|
///
|
||||||
|
/// https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#339
|
||||||
|
fn calc_hpet_ref(mut deltatsc: u64, ref1: u64, mut ref2: u64) -> u64 {
|
||||||
|
if ref2 <= ref1 {
|
||||||
|
ref2 += 0x100000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref2 -= ref1;
|
||||||
|
let mut tmp = ref2 * hpet_instance().period();
|
||||||
|
|
||||||
|
tmp /= 1000000;
|
||||||
|
|
||||||
|
deltatsc /= tmp;
|
||||||
|
|
||||||
|
return deltatsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据PMtimer的参考值计算tsc的频率
|
||||||
|
fn calc_pmtimer_ref(mut deltatsc: u64, ref1: u64, mut ref2: u64) -> u64 {
|
||||||
|
if unlikely(ref1 == 0 && ref2 == 0) {
|
||||||
|
return u64::MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref2 < ref1 {
|
||||||
|
ref2 += ACPI_PM_OVERRUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref2 -= ref1;
|
||||||
|
|
||||||
|
let mut tmp = ref2 * 1000000000;
|
||||||
|
|
||||||
|
tmp /= PMTMR_TICKS_PER_SEC;
|
||||||
|
|
||||||
|
deltatsc /= tmp;
|
||||||
|
|
||||||
|
return deltatsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tsc_khz() -> u64 {
|
||||||
|
unsafe { TSC_KHZ }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_khz() -> u64 {
|
||||||
|
unsafe { CPU_KHZ }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cpu_khz(khz: u64) {
|
||||||
|
unsafe {
|
||||||
|
CPU_KHZ = khz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_tsc_khz(khz: u64) {
|
||||||
|
unsafe {
|
||||||
|
TSC_KHZ = khz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ pub mod asm;
|
|||||||
mod acpi;
|
mod acpi;
|
||||||
mod c_adapter;
|
mod c_adapter;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
|
pub mod driver;
|
||||||
pub mod fpu;
|
pub mod fpu;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
@ -17,6 +18,7 @@ pub mod sched;
|
|||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod smp;
|
pub mod smp;
|
||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
pub use self::pci::pci::X86_64PciArch as PciArch;
|
pub use self::pci::pci::X86_64PciArch as PciArch;
|
||||||
|
|
||||||
@ -29,3 +31,4 @@ pub use crate::arch::asm::pio::X86_64PortIOArch as CurrentPortIOArch;
|
|||||||
pub use kvm::X86_64KVMArch as KVMArch;
|
pub use kvm::X86_64KVMArch as KVMArch;
|
||||||
|
|
||||||
pub use crate::arch::ipc::signal::X86_64SignalArch as CurrentSignalArch;
|
pub use crate::arch::ipc::signal::X86_64SignalArch as CurrentSignalArch;
|
||||||
|
pub use crate::arch::time::X86_64TimeArch as CurrentTimeArch;
|
||||||
|
9
kernel/src/arch/x86_64/time.rs
Normal file
9
kernel/src/arch/x86_64/time.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use crate::time::TimeArch;
|
||||||
|
|
||||||
|
pub struct X86_64TimeArch;
|
||||||
|
|
||||||
|
impl TimeArch for X86_64TimeArch {
|
||||||
|
fn get_cycles() -> usize {
|
||||||
|
unsafe { x86::time::rdtsc() as usize }
|
||||||
|
}
|
||||||
|
}
|
@ -59,17 +59,3 @@ struct cpu_core_info_t
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern struct cpu_core_info_t cpu_core_info[MAX_CPU_NUM];
|
extern struct cpu_core_info_t cpu_core_info[MAX_CPU_NUM];
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前cpu核心晶振频率
|
|
||||||
*
|
|
||||||
* @return uint32_t 当前cpu核心晶振频率
|
|
||||||
*/
|
|
||||||
uint32_t cpu_get_core_crysral_freq();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取处理器的tsc频率(单位:hz)
|
|
||||||
*
|
|
||||||
* @return uint64_t
|
|
||||||
*/
|
|
||||||
uint64_t cpu_get_tsc_freq();
|
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
//引入对bool类型的支持
|
// 引入对bool类型的支持
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <DragonOS/stdint.h>
|
#include <DragonOS/stdint.h>
|
||||||
#include <common/stddef.h>
|
#include <common/stddef.h>
|
||||||
@ -13,7 +13,6 @@
|
|||||||
#include <common/compiler.h>
|
#include <common/compiler.h>
|
||||||
#include <common/list.h>
|
#include <common/list.h>
|
||||||
|
|
||||||
|
|
||||||
#include <asm/asm.h>
|
#include <asm/asm.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +63,6 @@ static __always_inline ul ALIGN(const ul addr, const ul _align)
|
|||||||
return (ul)((addr + _align - 1) & (~(_align - 1)));
|
return (ul)((addr + _align - 1) & (~(_align - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void *memset(void *dst, unsigned char C, ul size)
|
void *memset(void *dst, unsigned char C, ul size)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -189,7 +187,6 @@ void io_out32(unsigned short port, unsigned int value)
|
|||||||
__asm__ __volatile__("cld;rep;outsw;mfence;" ::"d"(port), "S"(buffer), "c"(nr) \
|
__asm__ __volatile__("cld;rep;outsw;mfence;" ::"d"(port), "S"(buffer), "c"(nr) \
|
||||||
: "memory")
|
: "memory")
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 验证地址空间是否为用户地址空间
|
* @brief 验证地址空间是否为用户地址空间
|
||||||
*
|
*
|
||||||
@ -245,7 +242,6 @@ static inline uint64_t copy_from_user(void *dst, void *src, uint64_t size)
|
|||||||
*/
|
*/
|
||||||
static inline uint64_t copy_to_user(void *dst, void *src, uint64_t size)
|
static inline uint64_t copy_to_user(void *dst, void *src, uint64_t size)
|
||||||
{
|
{
|
||||||
uint64_t tmp0, tmp1;
|
|
||||||
if (verify_area((uint64_t)src, size))
|
if (verify_area((uint64_t)src, size))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -262,7 +258,7 @@ static inline uint64_t copy_to_user(void *dst, void *src, uint64_t size)
|
|||||||
// : "=&c"(size), "=&D"(tmp0), "=&S"(tmp1)
|
// : "=&c"(size), "=&D"(tmp0), "=&S"(tmp1)
|
||||||
// : "r"(size & 7), "0"(size >> 3), "1"(dst), "2"(src)
|
// : "r"(size & 7), "0"(size >> 3), "1"(dst), "2"(src)
|
||||||
// : "memory");
|
// : "memory");
|
||||||
memcpy(dst,src,size);
|
memcpy(dst, src, size);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@ -285,7 +281,6 @@ static __always_inline void __write8b(uint64_t vaddr, uint64_t value)
|
|||||||
{
|
{
|
||||||
asm volatile("movq %%rdx, 0(%%rax)" ::"a"(vaddr), "d"(value)
|
asm volatile("movq %%rdx, 0(%%rax)" ::"a"(vaddr), "d"(value)
|
||||||
: "memory");
|
: "memory");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -299,7 +294,6 @@ static __always_inline void __write4b(uint64_t vaddr, uint32_t value)
|
|||||||
{
|
{
|
||||||
asm volatile("movl %%edx, 0(%%rax)" ::"a"(vaddr), "d"(value)
|
asm volatile("movl %%edx, 0(%%rax)" ::"a"(vaddr), "d"(value)
|
||||||
: "memory");
|
: "memory");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,6 +22,7 @@ pub mod bus;
|
|||||||
mod c_adapter;
|
mod c_adapter;
|
||||||
pub mod glue;
|
pub mod glue;
|
||||||
pub mod old;
|
pub mod old;
|
||||||
|
pub mod pmtmr;
|
||||||
mod sysfs;
|
mod sysfs;
|
||||||
|
|
||||||
static mut __ACPI_TABLE: Option<acpi::AcpiTables<AcpiHandlerImpl>> = None;
|
static mut __ACPI_TABLE: Option<acpi::AcpiTables<AcpiHandlerImpl>> = None;
|
||||||
|
4
kernel/src/driver/acpi/pmtmr.rs
Normal file
4
kernel/src/driver/acpi/pmtmr.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub const ACPI_PM_OVERRUN: u64 = 1 << 24;
|
||||||
|
|
||||||
|
/// Number of PMTMR ticks expected during calibration run
|
||||||
|
pub const PMTMR_TICKS_PER_SEC: u64 = 3579545;
|
@ -11,6 +11,9 @@ static spinlock_t apic_timer_init_lock = {1};
|
|||||||
// bsp 是否已经完成apic时钟初始化
|
// bsp 是否已经完成apic时钟初始化
|
||||||
static bool bsp_initialized = false;
|
static bool bsp_initialized = false;
|
||||||
|
|
||||||
|
extern uint64_t rs_get_cycles();
|
||||||
|
extern uint64_t rs_tsc_get_cpu_khz();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化AP核的apic时钟
|
* @brief 初始化AP核的apic时钟
|
||||||
*
|
*
|
||||||
@ -59,7 +62,15 @@ uint64_t apic_timer_install(ul irq_num, void *arg)
|
|||||||
io_mfence();
|
io_mfence();
|
||||||
|
|
||||||
// 设置初始计数
|
// 设置初始计数
|
||||||
apic_timer_set_init_cnt(*(uint64_t *)arg);
|
|
||||||
|
uint64_t cpu_khz = rs_tsc_get_cpu_khz();
|
||||||
|
// 疑惑:这里使用khz吗?
|
||||||
|
// 我觉得应该是hz,但是由于旧的代码是测量出initcnt的,而不是计算的
|
||||||
|
// 然后我发现使用hz会导致计算出来的initcnt太大,导致系统卡顿,而khz的却能跑
|
||||||
|
// TODO: 这里需要进一步研究
|
||||||
|
uint64_t init_cnt = cpu_khz * APIC_TIMER_INTERVAL / (1000 * APIC_TIMER_DIVISOR);
|
||||||
|
kdebug("cpu_khz: %ld, init_cnt: %ld", cpu_khz, init_cnt);
|
||||||
|
apic_timer_set_init_cnt(init_cnt);
|
||||||
io_mfence();
|
io_mfence();
|
||||||
// 填写LVT
|
// 填写LVT
|
||||||
apic_timer_set_LVT(APIC_TIMER_IRQ_NUM, 1, APIC_LVT_Timer_Periodic);
|
apic_timer_set_LVT(APIC_TIMER_IRQ_NUM, 1, APIC_LVT_Timer_Periodic);
|
||||||
@ -101,17 +112,11 @@ void apic_timer_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
|
|||||||
void apic_timer_init()
|
void apic_timer_init()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (apic_timer_ticks_result == 0)
|
|
||||||
{
|
|
||||||
kBUG("APIC timer ticks in 5ms is equal to ZERO!");
|
|
||||||
while (1)
|
|
||||||
hlt();
|
|
||||||
}
|
|
||||||
uint64_t flags = 0;
|
uint64_t flags = 0;
|
||||||
spin_lock_irqsave(&apic_timer_init_lock, flags);
|
spin_lock_irqsave(&apic_timer_init_lock, flags);
|
||||||
kinfo("Initializing apic timer for cpu %d", rs_current_pcb_cpuid());
|
kinfo("Initializing apic timer for cpu %d", rs_current_pcb_cpuid());
|
||||||
io_mfence();
|
io_mfence();
|
||||||
irq_register(APIC_TIMER_IRQ_NUM, &apic_timer_ticks_result, &apic_timer_handler, 0, &apic_timer_intr_controller,
|
irq_register(APIC_TIMER_IRQ_NUM, NULL, &apic_timer_handler, 0, &apic_timer_intr_controller,
|
||||||
"apic timer");
|
"apic timer");
|
||||||
io_mfence();
|
io_mfence();
|
||||||
if (rs_current_pcb_cpuid() == 0)
|
if (rs_current_pcb_cpuid() == 0)
|
||||||
|
@ -13,7 +13,7 @@ use crate::include::bindings::bindings::{
|
|||||||
c_irq_install, c_irq_uninstall, pt_regs, ul, EAGAIN, EINVAL,
|
c_irq_install, c_irq_uninstall, pt_regs, ul, EAGAIN, EINVAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::libs::volatile::{volread, volwrite, Volatile, VolatileReadable, VolatileWritable};
|
use crate::libs::volatile::{volread, volwrite, Volatile};
|
||||||
|
|
||||||
/// MSIX表的一项
|
/// MSIX表的一项
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -1,283 +0,0 @@
|
|||||||
#include "HPET.h"
|
|
||||||
#include <common/kprint.h>
|
|
||||||
#include <common/compiler.h>
|
|
||||||
#include <mm/mm.h>
|
|
||||||
#include <driver/interrupt/apic/apic.h>
|
|
||||||
#include <exception/softirq.h>
|
|
||||||
#include <time/timer.h>
|
|
||||||
#include <process/process.h>
|
|
||||||
#include <sched/sched.h>
|
|
||||||
#include <smp/ipi.h>
|
|
||||||
#include <driver/interrupt/apic/apic_timer.h>
|
|
||||||
#include <common/spinlock.h>
|
|
||||||
#include <process/preempt.h>
|
|
||||||
|
|
||||||
#pragma GCC push_options
|
|
||||||
#pragma GCC optimize("O0")
|
|
||||||
static struct acpi_HPET_description_table_t *hpet_table;
|
|
||||||
static uint64_t HPET_REG_BASE = 0;
|
|
||||||
static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
|
|
||||||
static uint64_t HPET_freq = 0; // 主计时器频率
|
|
||||||
static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
|
|
||||||
static char measure_apic_timer_flag; // 初始化apic时钟时所用到的标志变量
|
|
||||||
|
|
||||||
// 测定tsc频率的临时变量
|
|
||||||
static uint64_t test_tsc_start = 0;
|
|
||||||
static uint64_t test_tsc_end = 0;
|
|
||||||
extern uint64_t Cpu_tsc_freq; // 导出自cpu.c
|
|
||||||
|
|
||||||
extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
|
|
||||||
extern uint64_t rs_update_timer_jiffies(uint64_t);
|
|
||||||
|
|
||||||
extern uint32_t rs_current_pcb_pid();
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
GCAP_ID = 0x00,
|
|
||||||
GEN_CONF = 0x10,
|
|
||||||
GINTR_STA = 0x20,
|
|
||||||
MAIN_CNT = 0xf0,
|
|
||||||
TIM0_CONF = 0x100,
|
|
||||||
TIM0_COMP = 0x108,
|
|
||||||
TIM1_CONF = 0x120,
|
|
||||||
TIM1_COMP = 0x128,
|
|
||||||
TIM2_CONF = 0x140,
|
|
||||||
TIM2_COMP = 0x148,
|
|
||||||
TIM3_CONF = 0x160,
|
|
||||||
TIM3_COMP = 0x168,
|
|
||||||
TIM4_CONF = 0x180,
|
|
||||||
TIM4_COMP = 0x188,
|
|
||||||
TIM5_CONF = 0x1a0,
|
|
||||||
TIM5_COMP = 0x1a8,
|
|
||||||
TIM6_CONF = 0x1c0,
|
|
||||||
TIM6_COMP = 0x1c8,
|
|
||||||
TIM7_CONF = 0x1e0,
|
|
||||||
TIM7_COMP = 0x1e8,
|
|
||||||
};
|
|
||||||
|
|
||||||
hardware_intr_controller HPET_intr_controller =
|
|
||||||
{
|
|
||||||
.enable = apic_ioapic_enable,
|
|
||||||
.disable = apic_ioapic_disable,
|
|
||||||
.install = apic_ioapic_install,
|
|
||||||
.uninstall = apic_ioapic_uninstall,
|
|
||||||
.ack = apic_ioapic_edge_ack,
|
|
||||||
};
|
|
||||||
|
|
||||||
void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
// printk("(HPET)");
|
|
||||||
switch (param)
|
|
||||||
{
|
|
||||||
case 0: // 定时器0中断
|
|
||||||
rs_update_timer_jiffies(HPET0_INTERVAL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
// 将HEPT中断消息转发到ap:1处理器
|
|
||||||
ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8,
|
|
||||||
ICR_APIC_FIXED, ICR_ALL_EXCLUDE_Self, true, 0);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 若当前时间比定时任务的时间间隔大,则进入中断下半部
|
|
||||||
if (rs_timer_get_first_expire() <= rs_clock())
|
|
||||||
rs_raise_softirq(TIMER_SIRQ);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
kwarn("Unsupported HPET irq: %d.", number);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 测定apic定时器以及tsc的频率的中断回调函数
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void HPET_measure_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
test_tsc_end = rdtsc();
|
|
||||||
// 停止apic定时器
|
|
||||||
// 写入每1ms的ticks
|
|
||||||
apic_timer_stop();
|
|
||||||
apic_timer_ticks_result = 0xFFFFFFFF - apic_timer_get_current();
|
|
||||||
measure_apic_timer_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 测定apic定时器以及tsc的频率
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void HPET_measure_freq()
|
|
||||||
{
|
|
||||||
rs_preempt_disable();
|
|
||||||
kinfo("Measuring local APIC timer's frequency...");
|
|
||||||
const uint64_t interval = APIC_TIMER_INTERVAL; // 测量给定时间内的计数
|
|
||||||
struct apic_IO_APIC_RTE_entry entry;
|
|
||||||
|
|
||||||
// 使用I/O APIC 的IRQ2接收hpet定时器0的中断
|
|
||||||
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
|
|
||||||
|
|
||||||
// 计算HPET0间隔多少个时钟周期触发一次中断
|
|
||||||
uint64_t clks_to_intr = 0.001 * interval * HPET_freq;
|
|
||||||
// kdebug("clks_to_intr=%#ld", clks_to_intr);
|
|
||||||
if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
|
|
||||||
{
|
|
||||||
kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
|
|
||||||
while (1)
|
|
||||||
hlt();
|
|
||||||
}
|
|
||||||
__write8b(HPET_REG_BASE + MAIN_CNT, 0);
|
|
||||||
io_mfence();
|
|
||||||
__write8b((HPET_REG_BASE + TIM0_CONF), 0x0044); // 设置定时器0为非周期,边沿触发,默认投递到IO APIC的2号引脚
|
|
||||||
io_mfence();
|
|
||||||
__write8b(HPET_REG_BASE + TIM0_COMP, clks_to_intr);
|
|
||||||
|
|
||||||
io_mfence();
|
|
||||||
|
|
||||||
measure_apic_timer_flag = false;
|
|
||||||
|
|
||||||
// 注册中断
|
|
||||||
irq_register(34, &entry, &HPET_measure_handler, 0, &HPET_intr_controller, "HPET0 measure");
|
|
||||||
sti();
|
|
||||||
|
|
||||||
// 设置div16
|
|
||||||
apic_timer_stop();
|
|
||||||
apic_timer_set_div(APIC_TIMER_DIVISOR);
|
|
||||||
|
|
||||||
// 设置初始计数
|
|
||||||
apic_timer_set_init_cnt(0xFFFFFFFF);
|
|
||||||
|
|
||||||
// 启动apic定时器
|
|
||||||
apic_timer_set_LVT(151, 0, APIC_LVT_Timer_One_Shot);
|
|
||||||
__write8b(HPET_REG_BASE + GEN_CONF, 3); // 置位旧设备中断路由兼容标志位、定时器组使能标志位,开始计时
|
|
||||||
|
|
||||||
// 顺便测定tsc频率
|
|
||||||
test_tsc_start = rdtsc();
|
|
||||||
io_mfence();
|
|
||||||
while (measure_apic_timer_flag == false)
|
|
||||||
;
|
|
||||||
|
|
||||||
irq_unregister(34);
|
|
||||||
|
|
||||||
*(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 0; // 停用HPET定时器
|
|
||||||
io_mfence();
|
|
||||||
kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticks_result);
|
|
||||||
// 计算tsc频率
|
|
||||||
Cpu_tsc_freq = (test_tsc_end - test_tsc_start) * (1000UL / interval);
|
|
||||||
|
|
||||||
kinfo("TSC frequency: %ldMHz", Cpu_tsc_freq / 1000000);
|
|
||||||
|
|
||||||
rs_preempt_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用HPET周期中断(5ms)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void HPET_enable()
|
|
||||||
{
|
|
||||||
struct apic_IO_APIC_RTE_entry entry;
|
|
||||||
// 使用I/O APIC 的IRQ2接收hpet定时器0的中断
|
|
||||||
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
|
|
||||||
|
|
||||||
// 计算HPET0间隔多少个时钟周期触发一次中断
|
|
||||||
uint64_t clks_to_intr = 0.000001 * HPET0_INTERVAL * HPET_freq;
|
|
||||||
// kdebug("clks_to_intr=%#ld", clks_to_intr);
|
|
||||||
if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
|
|
||||||
{
|
|
||||||
kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
|
|
||||||
while (1)
|
|
||||||
hlt();
|
|
||||||
}
|
|
||||||
// kdebug("[HPET0] conf register=%#018lx conf register[63:32]=%#06lx", (*(uint64_t *)(HPET_REG_BASE + TIM0_CONF)), ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))>>32)&0xffffffff);
|
|
||||||
__write8b(HPET_REG_BASE + MAIN_CNT, 0);
|
|
||||||
io_mfence();
|
|
||||||
__write8b(HPET_REG_BASE + TIM0_CONF, 0x004c); // 设置定时器0为周期定时,边沿触发,默认投递到IO APIC的2号引脚(看conf寄存器的高32bit,哪一位被置1,则可以投递到哪一个I/O apic引脚)
|
|
||||||
io_mfence();
|
|
||||||
__write8b(HPET_REG_BASE + TIM0_COMP, clks_to_intr);
|
|
||||||
|
|
||||||
io_mfence();
|
|
||||||
|
|
||||||
// kdebug("[HPET0] conf register after modify=%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))));
|
|
||||||
// kdebug("[HPET1] conf register =%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM1_CONF))));
|
|
||||||
|
|
||||||
// 注册中断
|
|
||||||
irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
|
|
||||||
io_mfence();
|
|
||||||
__write8b(HPET_REG_BASE + GEN_CONF, 3); // 置位旧设备中断路由兼容标志位、定时器组使能标志位
|
|
||||||
kinfo("HPET0 enabled.");
|
|
||||||
|
|
||||||
io_mfence();
|
|
||||||
}
|
|
||||||
|
|
||||||
int HPET_init()
|
|
||||||
{
|
|
||||||
kinfo("Initializing HPET...");
|
|
||||||
// 从acpi获取hpet结构体
|
|
||||||
ul hpet_table_addr = 0;
|
|
||||||
acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
|
|
||||||
|
|
||||||
// ACPI表没有HPET,尝试读HPTC
|
|
||||||
if (hpet_table_addr == 0)
|
|
||||||
{
|
|
||||||
kwarn("ACPI: HPET Table Not Found On This Computer!");
|
|
||||||
|
|
||||||
if (RCBA_vaddr != 0)
|
|
||||||
{
|
|
||||||
kerror("NO HPET found on this computer!");
|
|
||||||
uint64_t hptc_vaddr = (RCBA_vaddr + 0x3404UL);
|
|
||||||
// enable HPET
|
|
||||||
io_mfence();
|
|
||||||
// 读取HPET配置寄存器地址
|
|
||||||
switch (__read4b(hptc_vaddr) & 0x3)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed01000;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed02000;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed03000;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// enable HPET
|
|
||||||
__write4b(hptc_vaddr, 0x80);
|
|
||||||
io_mfence();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 没有RCBA寄存器,采用默认值
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
|
|
||||||
kwarn("There is no RCBA register on this computer, and HPET regs base use default value.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // ACPI表中有HPET表
|
|
||||||
{
|
|
||||||
hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
|
|
||||||
// kdebug("hpet_table_addr=%#018lx", hpet_table_addr);
|
|
||||||
|
|
||||||
// 由于这段内存与io/apic的映射在同一物理页内,因此不需要重复映射
|
|
||||||
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
|
|
||||||
kdebug("hpet_table->address=%#018lx", hpet_table->address);
|
|
||||||
}
|
|
||||||
kdebug("HPET_REG_BASE=%#018lx", HPET_REG_BASE);
|
|
||||||
|
|
||||||
// 读取计时精度并计算频率
|
|
||||||
uint64_t tmp;
|
|
||||||
tmp = __read8b(HPET_REG_BASE + GCAP_ID);
|
|
||||||
HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
|
|
||||||
HPET_freq = 1e15 / HPET_COUNTER_CLK_PERIOD;
|
|
||||||
HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
|
|
||||||
|
|
||||||
kdebug("HPET_COUNTER_CLK_PERIOD=%#018lx", HPET_COUNTER_CLK_PERIOD);
|
|
||||||
kinfo("Total HPET timers: %d", HPET_NUM_TIM_CAP);
|
|
||||||
|
|
||||||
kinfo("HPET driver Initialized.");
|
|
||||||
}
|
|
||||||
#pragma GCC pop_options
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <common/glib.h>
|
|
||||||
#include <driver/acpi/acpi.h>
|
|
||||||
|
|
||||||
#define E_HPET_INIT_FAILED 1
|
|
||||||
|
|
||||||
#define HPET0_INTERVAL 500 // HPET0定时器的中断间隔为500us
|
|
||||||
int HPET_init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 测定apic定时器以及tsc的频率
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void HPET_measure_freq();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用HPET周期中断(5ms)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void HPET_enable();
|
|
@ -1,6 +1,6 @@
|
|||||||
all: HPET.o
|
all:
|
||||||
|
|
||||||
CFLAGS += -I .
|
CFLAGS += -I .
|
||||||
|
|
||||||
HPET.o: HPET/HPET.c
|
#HPET.o: HPET/HPET.c
|
||||||
$(CC) $(CFLAGS) -c HPET/HPET.c -o HPET/HPET.o
|
# $(CC) $(CFLAGS) -c HPET/HPET.c -o HPET/HPET.o
|
||||||
|
73
kernel/src/driver/timers/hpet/mod.rs
Normal file
73
kernel/src/driver/timers/hpet/mod.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::libs::volatile::Volatile;
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct HpetRegisters {
|
||||||
|
capabilties: Volatile<u32>,
|
||||||
|
period: Volatile<u32>,
|
||||||
|
_reserved0: Volatile<u64>,
|
||||||
|
general_config: Volatile<u64>,
|
||||||
|
_reserved1: Volatile<u64>,
|
||||||
|
general_intr_status: Volatile<u64>,
|
||||||
|
_reserved2: [Volatile<u64>; 25],
|
||||||
|
main_counter_value: Volatile<u64>,
|
||||||
|
_reserved3: Volatile<u64>,
|
||||||
|
// 这里后面跟着各个定时器的寄存器(数量由capabilties决定)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HpetRegisters {
|
||||||
|
/// 获取 HPET Timer 的数量
|
||||||
|
pub fn timers_num(&self) -> usize {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
let cap = unsafe { volread!(p, capabilties) };
|
||||||
|
(cap >> 8) as usize & 0x1f
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 HPET 计数器的周期
|
||||||
|
pub fn counter_clock_period(&self) -> u64 {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
let period = unsafe { volread!(p, period) };
|
||||||
|
period as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 HPET 计数器的频率
|
||||||
|
pub fn frequency(&self) -> u64 {
|
||||||
|
10000_0000_0000_000 / self.counter_clock_period()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_counter_value(&self) -> u64 {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
let main_counter_value = unsafe { volread!(p, main_counter_value) };
|
||||||
|
main_counter_value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_main_counter_value(&mut self, value: u64) {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
volwrite!(p, main_counter_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn general_config(&self) -> u64 {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
unsafe { volread!(p, general_config) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_general_config(&mut self, value: u64) {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
volwrite!(p, general_config, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn general_intr_status(&self) -> u64 {
|
||||||
|
let p = NonNull::new(self as *const HpetRegisters as *mut HpetRegisters).unwrap();
|
||||||
|
unsafe { volread!(p, general_intr_status) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct HpetTimerRegisters {
|
||||||
|
pub config: Volatile<u64>,
|
||||||
|
pub comparator_value: Volatile<u64>,
|
||||||
|
pub fsb_interrupt_route: [Volatile<u64>; 2],
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
|
pub mod hpet;
|
||||||
pub mod rtc;
|
pub mod rtc;
|
||||||
|
@ -100,32 +100,3 @@ void cpu_cpuid(uint32_t mop, uint32_t sop, uint32_t *eax, uint32_t *ebx, uint32_
|
|||||||
: "0"(mop), "2"(sop)
|
: "0"(mop), "2"(sop)
|
||||||
: "memory");
|
: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前cpu核心晶振频率(是一个Write-on-box的值)
|
|
||||||
*
|
|
||||||
* hint: 某些cpu无法提供该数据,返回值为0
|
|
||||||
* @return uint32_t 当前cpu核心晶振频率
|
|
||||||
*/
|
|
||||||
uint32_t cpu_get_core_crysral_freq()
|
|
||||||
{
|
|
||||||
uint32_t a = 0, b = 0, c = 0, d = 0;
|
|
||||||
|
|
||||||
// cpu_cpuid(0x15, 0, &a, &b, &c, &d);
|
|
||||||
__asm__ __volatile__("cpuid \n\t"
|
|
||||||
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
|
|
||||||
: "0"(0x15), "2"(0)
|
|
||||||
: "memory");
|
|
||||||
// kdebug("Cpu_cpuid_max_Basic_mop = %#03x, a=%ld, b=%ld, c=%ld, d=%ld", Cpu_cpuid_max_Basic_mop, a, b, c, d);
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief 获取处理器的tsc频率(单位:hz)
|
|
||||||
*
|
|
||||||
* @return uint64_t
|
|
||||||
*/
|
|
||||||
uint64_t cpu_get_tsc_freq()
|
|
||||||
{
|
|
||||||
return Cpu_tsc_freq;
|
|
||||||
}
|
|
||||||
|
@ -1002,6 +1002,43 @@ pub fn textui_putchar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 向默认窗口输出一个字符串
|
||||||
|
pub fn textui_putstr(
|
||||||
|
string: &str,
|
||||||
|
fr_color: FontColor,
|
||||||
|
bk_color: FontColor,
|
||||||
|
) -> Result<(), SystemError> {
|
||||||
|
let window = if unsafe { TEXTUI_IS_INIT } {
|
||||||
|
let fw = textui_framework();
|
||||||
|
let w = fw.current_window.clone();
|
||||||
|
Some(w)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut guard = window.as_ref().map(|w| w.lock());
|
||||||
|
|
||||||
|
for character in string.chars() {
|
||||||
|
if unsafe { TEXTUI_IS_INIT } {
|
||||||
|
guard.as_mut().unwrap().textui_putchar_window(
|
||||||
|
character,
|
||||||
|
fr_color,
|
||||||
|
bk_color,
|
||||||
|
ENABLE_PUT_TO_WINDOW.load(Ordering::SeqCst),
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
no_init_textui_putchar_window(
|
||||||
|
character,
|
||||||
|
fr_color,
|
||||||
|
bk_color,
|
||||||
|
ENABLE_PUT_TO_WINDOW.load(Ordering::SeqCst),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
/// 初始化text ui框架
|
/// 初始化text ui框架
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
use super::lib_ui::textui::{textui_putchar, FontColor};
|
use super::lib_ui::textui::{textui_putstr, FontColor};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
@ -77,15 +77,11 @@ impl PrintkWriter {
|
|||||||
/// 并输出白底黑字
|
/// 并输出白底黑字
|
||||||
/// @param str: 要写入的字符
|
/// @param str: 要写入的字符
|
||||||
pub fn __write_string(&mut self, s: &str) {
|
pub fn __write_string(&mut self, s: &str) {
|
||||||
for c in s.chars() {
|
textui_putstr(s, FontColor::WHITE, FontColor::BLACK).ok();
|
||||||
textui_putchar(c, FontColor::WHITE, FontColor::BLACK).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __write_string_color(&self, fr_color: FontColor, bk_color: FontColor, s: &str) {
|
pub fn __write_string_color(&self, fr_color: FontColor, bk_color: FontColor, s: &str) {
|
||||||
for c in s.chars() {
|
textui_putstr(s, fr_color, bk_color).ok();
|
||||||
textui_putchar(c, fr_color, bk_color).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,9 @@ impl<T: Copy> VolatileWritable<T> for *mut Volatile<T> {
|
|||||||
/// ```
|
/// ```
|
||||||
macro_rules! volread {
|
macro_rules! volread {
|
||||||
($nonnull:expr, $field:ident) => {
|
($nonnull:expr, $field:ident) => {
|
||||||
VolatileReadable::vread(core::ptr::addr_of!((*$nonnull.as_ptr()).$field))
|
crate::libs::volatile::VolatileReadable::vread(core::ptr::addr_of!(
|
||||||
|
(*$nonnull.as_ptr()).$field
|
||||||
|
))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +168,10 @@ macro_rules! volread {
|
|||||||
/// ```
|
/// ```
|
||||||
macro_rules! volwrite {
|
macro_rules! volwrite {
|
||||||
($nonnull:expr, $field:ident, $value:expr) => {
|
($nonnull:expr, $field:ident, $value:expr) => {
|
||||||
VolatileWritable::vwrite(core::ptr::addr_of_mut!((*$nonnull.as_ptr()).$field), $value)
|
crate::libs::volatile::VolatileWritable::vwrite(
|
||||||
|
core::ptr::addr_of_mut!((*$nonnull.as_ptr()).$field),
|
||||||
|
$value,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "driver/keyboard/ps2_keyboard.h"
|
#include "driver/keyboard/ps2_keyboard.h"
|
||||||
#include "driver/mouse/ps2_mouse.h"
|
#include "driver/mouse/ps2_mouse.h"
|
||||||
#include "driver/multiboot2/multiboot2.h"
|
#include "driver/multiboot2/multiboot2.h"
|
||||||
#include <driver/timers/HPET/HPET.h>
|
|
||||||
#include <time/timer.h>
|
#include <time/timer.h>
|
||||||
|
|
||||||
#include <driver/interrupt/apic/apic_timer.h>
|
#include <driver/interrupt/apic/apic_timer.h>
|
||||||
@ -39,6 +38,9 @@ extern void rs_kthread_init();
|
|||||||
extern void rs_init_intertrait();
|
extern void rs_init_intertrait();
|
||||||
extern void rs_init_before_mem_init();
|
extern void rs_init_before_mem_init();
|
||||||
extern int rs_setup_arch();
|
extern int rs_setup_arch();
|
||||||
|
extern int rs_hpet_init();
|
||||||
|
extern int rs_hpet_enable();
|
||||||
|
extern int rs_tsc_init();
|
||||||
|
|
||||||
ul bsp_idt_size, bsp_gdt_size;
|
ul bsp_idt_size, bsp_gdt_size;
|
||||||
|
|
||||||
@ -149,15 +151,11 @@ void system_initialize()
|
|||||||
io_mfence();
|
io_mfence();
|
||||||
smp_init();
|
smp_init();
|
||||||
|
|
||||||
io_mfence();
|
|
||||||
|
|
||||||
HPET_init();
|
|
||||||
io_mfence();
|
|
||||||
HPET_measure_freq();
|
|
||||||
|
|
||||||
io_mfence();
|
io_mfence();
|
||||||
cli();
|
cli();
|
||||||
HPET_enable();
|
rs_hpet_init();
|
||||||
|
rs_hpet_enable();
|
||||||
|
rs_tsc_init();
|
||||||
|
|
||||||
io_mfence();
|
io_mfence();
|
||||||
|
|
||||||
|
@ -370,3 +370,8 @@ impl Into<smoltcp::time::Duration> for Duration {
|
|||||||
smoltcp::time::Duration::from_millis(self.millis())
|
smoltcp::time::Duration::from_millis(self.millis())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TimeArch {
|
||||||
|
/// Get CPU cycles (Read from register)
|
||||||
|
fn get_cycles() -> usize;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <common/glib.h>
|
#include <common/glib.h>
|
||||||
#include <driver/timers/HPET/HPET.h>
|
|
||||||
|
|
||||||
|
|
||||||
// 定义LONG_MAX为最大超时时间 - 允许负数
|
// 定义LONG_MAX为最大超时时间 - 允许负数
|
||||||
|
Loading…
x
Reference in New Issue
Block a user