feat: RTC initialized
This commit is contained in:
parent
bcf97c9e24
commit
b277334069
0
kernel/src/hal/apic.rs
Normal file
0
kernel/src/hal/apic.rs
Normal file
0
kernel/src/hal/ioapic.rs
Normal file
0
kernel/src/hal/ioapic.rs
Normal file
@ -1,4 +1,6 @@
|
||||
pub mod io;
|
||||
pub mod cpu;
|
||||
pub mod acpi;
|
||||
mod rtc;
|
||||
pub mod rtc;
|
||||
pub mod apic;
|
||||
mod ioapic;
|
||||
@ -0,0 +1,333 @@
|
||||
// kernel/src/hal/rtc.rs
|
||||
use crate::hal::io::{io_port_rb, io_port_wb};
|
||||
use spin::Mutex;
|
||||
use crate::log_info;
|
||||
|
||||
const RTC_INDEX_PORT: u16 = 0x70;
|
||||
const RTC_TARGET_PORT: u16 = 0x71;
|
||||
const WITH_NMI_DISABLED: u8 = 0x80;
|
||||
|
||||
const RTC_REG_SEC: u8 = 0x00;
|
||||
const RTC_REG_MIN: u8 = 0x02;
|
||||
const RTC_REG_HRS: u8 = 0x04;
|
||||
const RTC_REG_WDY: u8 = 0x06; // Weekday
|
||||
const RTC_REG_DAY: u8 = 0x07;
|
||||
const RTC_REG_MTH: u8 = 0x08;
|
||||
const RTC_REG_YRS: u8 = 0x09;
|
||||
|
||||
const RTC_REG_A: u8 = 0x0A;
|
||||
const RTC_REG_B: u8 = 0x0B;
|
||||
const RTC_REG_C: u8 = 0x0C;
|
||||
const RTC_REG_D: u8 = 0x0D;
|
||||
|
||||
const RTC_UPDATE_IN_PROGRESS: u8 = 0x80; // Status Register A bit 7
|
||||
const RTC_BIN_ENCODED_BIT: u8 = 0x04; // Status Register B bit 2
|
||||
const RTC_24HRS_ENCODED_BIT: u8 = 0x02; // Status Register B bit 1
|
||||
|
||||
const RTC_TIMER_ON: u8 = 0x40; // Enable periodic interrupt (bit 6)
|
||||
const RTC_FREQUENCY_1024HZ: u8 = 0b110; // Rate selector for 1024Hz
|
||||
const RTC_DIVIDER_33KHZ: u8 = 0b010 << 4; // 32.768kHz crystal divider
|
||||
const RTC_TIMER_BASE_FREQUENCY: u32 = 1024;
|
||||
|
||||
const RTC_CURRENT_CENTURY: u16 = 2000;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DateTime {
|
||||
pub year: u16,
|
||||
pub month: u8,
|
||||
pub day: u8,
|
||||
pub weekday: u8,
|
||||
pub hour: u8,
|
||||
pub minute: u8,
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
impl DateTime {
|
||||
pub fn format(&self) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
|
||||
self.year, self.month, self.day,
|
||||
self.hour, self.minute, self.second
|
||||
)
|
||||
}
|
||||
|
||||
pub fn weekday_name(&self) -> &'static str {
|
||||
match self.weekday {
|
||||
1 => "Sunday",
|
||||
2 => "Monday",
|
||||
3 => "Tuesday",
|
||||
4 => "Wednesday",
|
||||
5 => "Thursday",
|
||||
6 => "Friday",
|
||||
7 => "Saturday",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn month_name(&self) -> &'static str {
|
||||
match self.month {
|
||||
1 => "January",
|
||||
2 => "February",
|
||||
3 => "March",
|
||||
4 => "April",
|
||||
5 => "May",
|
||||
6 => "June",
|
||||
7 => "July",
|
||||
8 => "August",
|
||||
9 => "September",
|
||||
10 => "October",
|
||||
11 => "November",
|
||||
12 => "December",
|
||||
_ => "Unknown",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rtc {
|
||||
binary_mode: bool,
|
||||
hour_24_mode: bool,
|
||||
}
|
||||
|
||||
impl Rtc {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
binary_mode: false,
|
||||
hour_24_mode: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read CMOS registers (with NMI disabled)
|
||||
unsafe fn read_register(reg: u8) -> u8 {
|
||||
io_port_wb(RTC_INDEX_PORT, reg | WITH_NMI_DISABLED);
|
||||
io_port_rb(RTC_TARGET_PORT)
|
||||
}
|
||||
|
||||
/// Write to CMOS register (with NMI disabled)
|
||||
unsafe fn write_register(reg: u8, value: u8) {
|
||||
io_port_wb(RTC_INDEX_PORT, reg | WITH_NMI_DISABLED);
|
||||
io_port_wb(RTC_TARGET_PORT, value);
|
||||
}
|
||||
|
||||
/// Check if RTC is updating
|
||||
unsafe fn is_updating() -> bool {
|
||||
(Self::read_register(RTC_REG_A) & RTC_UPDATE_IN_PROGRESS) != 0
|
||||
}
|
||||
|
||||
/// Wait for RTC update to complete
|
||||
unsafe fn wait_for_update() {
|
||||
while Self::is_updating() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// 將 BCD 轉換為二進制
|
||||
fn bcd_to_binary(bcd: u8) -> u8 {
|
||||
(bcd & 0x0F) + ((bcd >> 4) * 10)
|
||||
}
|
||||
|
||||
/// Convert binary to BCD
|
||||
#[allow(dead_code)]
|
||||
fn binary_to_bcd(bin: u8) -> u8 {
|
||||
((bin / 10) << 4) | (bin % 10)
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
unsafe {
|
||||
// Read status register B
|
||||
let status_b = Self::read_register(RTC_REG_B);
|
||||
|
||||
self.binary_mode = (status_b & RTC_BIN_ENCODED_BIT) != 0;
|
||||
self.hour_24_mode = (status_b & RTC_24HRS_ENCODED_BIT) != 0;
|
||||
|
||||
crate::log_debug!(
|
||||
"RTC Mode: {} encoding, {} hour format",
|
||||
if self.binary_mode { "Binary" } else { "BCD" },
|
||||
if self.hour_24_mode { "24" } else { "12" }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read raw RTC time data
|
||||
unsafe fn read_raw(&self) -> (u8, u8, u8, u8, u8, u8, u8) {
|
||||
Self::wait_for_update();
|
||||
|
||||
let second = Self::read_register(RTC_REG_SEC);
|
||||
let minute = Self::read_register(RTC_REG_MIN);
|
||||
let hour = Self::read_register(RTC_REG_HRS);
|
||||
let day = Self::read_register(RTC_REG_DAY);
|
||||
let month = Self::read_register(RTC_REG_MTH);
|
||||
let year = Self::read_register(RTC_REG_YRS);
|
||||
let weekday = Self::read_register(RTC_REG_WDY);
|
||||
|
||||
(second, minute, hour, day, month, year, weekday)
|
||||
}
|
||||
|
||||
/// Convert the value
|
||||
fn convert_value(&self, value: u8) -> u8 {
|
||||
if self.binary_mode {
|
||||
value
|
||||
} else {
|
||||
Self::bcd_to_binary(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RTC time
|
||||
pub fn read_time(&self) -> DateTime {
|
||||
unsafe {
|
||||
let (mut second, mut minute, mut hour, mut day, mut month, mut year, weekday) =
|
||||
self.read_raw();
|
||||
|
||||
second = self.convert_value(second);
|
||||
minute = self.convert_value(minute);
|
||||
day = self.convert_value(day);
|
||||
month = self.convert_value(month);
|
||||
year = self.convert_value(year);
|
||||
|
||||
let pm_bit = hour & 0x80;
|
||||
hour = self.convert_value(hour & 0x7F);
|
||||
|
||||
if !self.hour_24_mode && pm_bit != 0 {
|
||||
hour = (hour + 12) % 24;
|
||||
}
|
||||
|
||||
let full_year = RTC_CURRENT_CENTURY + year as u16;
|
||||
|
||||
DateTime {
|
||||
year: full_year,
|
||||
month,
|
||||
day,
|
||||
weekday,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read multiple times and ensure consistency
|
||||
pub fn read_time_stable(&self) -> DateTime {
|
||||
loop {
|
||||
let time1 = self.read_time();
|
||||
let time2 = self.read_time();
|
||||
|
||||
if time1.second == time2.second
|
||||
&& time1.minute == time2.minute
|
||||
&& time1.hour == time2.hour
|
||||
{
|
||||
return time1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable RTC timer interrupt (1024Hz)
|
||||
pub fn enable_timer(&self) {
|
||||
unsafe {
|
||||
// Disable NMI and set frequency
|
||||
let mut reg_a = Self::read_register(RTC_REG_A);
|
||||
reg_a = (reg_a & 0xF0) | RTC_DIVIDER_33KHZ | RTC_FREQUENCY_1024HZ;
|
||||
Self::write_register(RTC_REG_A, reg_a);
|
||||
|
||||
// Enable periodic interrupts
|
||||
let mut reg_b = Self::read_register(RTC_REG_B);
|
||||
reg_b |= RTC_TIMER_ON;
|
||||
Self::write_register(RTC_REG_B, reg_b);
|
||||
|
||||
log_info!("RTC timer enabled at {}Hz", RTC_TIMER_BASE_FREQUENCY);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable RTC timer interrupt
|
||||
pub fn disable_timer(&self) {
|
||||
unsafe {
|
||||
let mut reg_b = Self::read_register(RTC_REG_B);
|
||||
reg_b &= !RTC_TIMER_ON;
|
||||
Self::write_register(RTC_REG_B, reg_b);
|
||||
|
||||
log_info!("RTC timer disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// Read and clear RTC interrupt status (must be called in the interrupt handler)
|
||||
pub fn read_interrupt_status(&self) -> u8 {
|
||||
unsafe { Self::read_register(RTC_REG_C) }
|
||||
}
|
||||
}
|
||||
|
||||
static RTC: Mutex<Option<Rtc>> = Mutex::new(None);
|
||||
|
||||
pub fn init() {
|
||||
let mut rtc = Rtc::new();
|
||||
rtc.init();
|
||||
*RTC.lock() = Some(rtc);
|
||||
|
||||
log_info!("RTC initialized");
|
||||
}
|
||||
|
||||
pub fn get_time() -> Option<DateTime> {
|
||||
let rtc = RTC.lock();
|
||||
let rtc = rtc.as_ref()?;
|
||||
Some(rtc.read_time_stable())
|
||||
}
|
||||
|
||||
pub fn print_info() {
|
||||
if let Some(time) = get_time() {
|
||||
log_info!(
|
||||
"{}, {} {}, {} - {:02}:{:02}:{:02}",
|
||||
time.weekday_name(),
|
||||
time.month_name(),
|
||||
time.day,
|
||||
time.year,
|
||||
time.hour,
|
||||
time.minute,
|
||||
time.second
|
||||
);
|
||||
} else {
|
||||
crate::log_warn!("RTC not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable RTC timer
|
||||
pub fn enable_timer() {
|
||||
if let Some(rtc) = RTC.lock().as_ref() {
|
||||
rtc.enable_timer();
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the RTC timer
|
||||
pub fn disable_timer() {
|
||||
if let Some(rtc) = RTC.lock().as_ref() {
|
||||
rtc.disable_timer();
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle RTC interrupt (needs to be called in IRQ 8 handler)
|
||||
pub fn handle_interrupt() {
|
||||
if let Some(rtc) = RTC.lock().as_ref() {
|
||||
// CRITICAL: Must read Register C to clear the interrupt flag
|
||||
// Otherwise the RTC will not send the next interrupt!
|
||||
let status = rtc.read_interrupt_status();
|
||||
|
||||
// bit 6 = periodic interrupt
|
||||
if (status & 0x40) != 0 {
|
||||
on_periodic_interrupt();
|
||||
}
|
||||
|
||||
// bit 5 = alarm interrupt
|
||||
if (status & 0x20) != 0 {
|
||||
on_alarm_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RTC periodic interrupt callback (can be overwritten by other modules)
|
||||
#[allow(dead_code)]
|
||||
fn on_periodic_interrupt() {
|
||||
// 在這裡處理定時器事件
|
||||
// 例如: 更新系統時間、調度任務等
|
||||
}
|
||||
|
||||
/// RTC alarm interrupt callback
|
||||
#[allow(dead_code)]
|
||||
fn on_alarm_interrupt() {
|
||||
// 在這裡處理鬧鐘事件
|
||||
}
|
||||
@ -11,7 +11,7 @@ use crate::kernel::k_main;
|
||||
use crate::klibc::logger::{init, LogLevel, LoggerConfig};
|
||||
use crate::klibc::malloc;
|
||||
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
|
||||
use crate::hal::acpi;
|
||||
use crate::hal::{acpi, rtc};
|
||||
|
||||
fn _logger_init() {
|
||||
init(
|
||||
@ -158,6 +158,8 @@ fn _post_init() {
|
||||
kprintln!();
|
||||
log_info!("Post Initialization");
|
||||
// TODO: 釋放 bootloader 佔用的內存
|
||||
|
||||
rtc::init();
|
||||
log_debug!("Cleanup completed");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// kernel/src/k_main.rs (Updated with Logger Demo)
|
||||
use crate::hal::cpu;
|
||||
// kernel/src/k_main.rs
|
||||
use crate::hal::{cpu, rtc};
|
||||
use crate::kprintln;
|
||||
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
|
||||
use crate::mm::{vma, vmm};
|
||||
@ -20,6 +20,11 @@ pub fn _kernel_main() -> ! {
|
||||
|
||||
vma::print_info();
|
||||
vmm::get_vmm_stats().print();
|
||||
|
||||
kprintln!();
|
||||
|
||||
rtc::print_info();
|
||||
|
||||
kprintln!();
|
||||
|
||||
log_debug!("CR0: 0x{:016x}", cpu::cpu_r_cr0());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user