1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
// ---------------------------------------------------------------------------
// RTC/CMOS clock — the standard PC real-time clock on ports 0x70/0x71.
//
// Reads the time registers (0x00-0x09). Values may be BCD or binary and
// hours may be 12- or 24-hour depending on status register B; we normalize
// to binary 24-hour. The century register (0x32) is used when it reports
// a sane value, otherwise we assume 2000+. NMI is masked (bit 7 set) while
// poking CMOS — this kernel has no NMI handler, so a stray NMI would fault.
// ---------------------------------------------------------------------------
pub struct DateTime {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
/// Read one CMOS register. Writes bit 7 to the address port, which masks
/// NMIs during the access.
fn cmos_read(reg: u8) -> u8 {
unsafe {
core::arch::asm!(
"out dx, al",
in("dx") 0x70u16,
in("al") reg | 0x80,
options(nomem, nostack, preserves_flags)
);
let val: u8;
core::arch::asm!(
"in al, dx",
out("al") val,
in("dx") 0x71u16,
options(nomem, nostack, preserves_flags)
);
val
}
}
fn bcd_to_bin(v: u8) -> u8 {
(v & 0x0F) + (v >> 4) * 10
}
/// Read the current wall-clock time from the RTC.
pub fn now() -> DateTime {
// Wait until an in-progress update finishes (status A, UIP bit 7).
while cmos_read(0x0A) & 0x80 != 0 {
core::hint::spin_loop();
}
let reg_b = cmos_read(0x0B);
let bcd = reg_b & 0x04 == 0; // bit 2: 0 = BCD, 1 = binary
let hour12 = reg_b & 0x02 == 0; // bit 1: 1 = 24-hour, 0 = 12-hour
let mut second = cmos_read(0x00);
let mut minute = cmos_read(0x02);
let mut hour = cmos_read(0x04);
let mut day = cmos_read(0x07);
let mut month = cmos_read(0x08);
let mut year = cmos_read(0x09);
let century = cmos_read(0x32);
if bcd {
second = bcd_to_bin(second);
minute = bcd_to_bin(minute);
hour = bcd_to_bin(hour);
day = bcd_to_bin(day);
month = bcd_to_bin(month);
year = bcd_to_bin(year);
}
if hour12 {
let pm = hour & 0x80 != 0; // top bit of a 12-hour hour is the PM flag
hour &= 0x7F;
if hour == 12 {
hour = 0; // 12 AM/PM are both 0 in 24-hour form
}
if pm {
hour += 12;
}
}
let year = {
let full = century as u16 * 100 + year as u16;
if (1970..=2200).contains(&full) {
full
} else {
// Unreadable century register (QEMU returns garbage here) —
// assume 2000+ for the 2-digit year.
2000 + year as u16
}
};
DateTime {
year,
month,
day,
hour,
minute,
second,
}
}
|