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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! Support for the HPET: High Precision Event Timer.

#![no_std]

use log::debug;
use volatile::{Volatile, ReadOnly};
use zerocopy::FromBytes;
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
use memory::{allocate_pages, allocate_frames_by_bytes_at, PageTable, PhysicalAddress, PteFlags, BorrowedMappedPages, Mutable};
use sdt::{Sdt, GenericAddressStructure};
use acpi_table::{AcpiTables, AcpiSignature};
use time::Instant;

/// The static instance of the HPET's ACPI memory region, which derefs to an Hpet instance.
static HPET: Once<RwLock<BorrowedMappedPages<Hpet, Mutable>>> = Once::new();


/// Returns a locked guard that immutably derefs to the HPET timer.
/// 
/// # Example
/// ```
/// let counter_val = get_hpet().as_ref().unwrap().get_counter();
/// ```
pub fn get_hpet() -> Option<RwLockReadGuard<'static, BorrowedMappedPages<Hpet, Mutable>>> {
    HPET.get().map(|h| h.read())
}

/// Returns a locked guard that mutably derefs to the HPET timer.
/// 
/// # Example
/// ```
/// get_hpet_mut().as_mut().unwrap().enable_counter(true);
/// ```
pub fn get_hpet_mut() -> Option<RwLockWriteGuard<'static, BorrowedMappedPages<Hpet, Mutable>>> {
    HPET.get().map(|h| h.write())
}


/// A structure that offers access to HPET through its I/O registers, 
/// specified by the format here: <https://wiki.osdev.org/HPET#HPET_registers>.
#[derive(FromBytes)]
#[repr(C)]
pub struct Hpet {
    /// The General Capabilities and ID Register, at offset 0x0.
    pub general_capabilities_and_id: ReadOnly<u64>,
    _padding0:                       u64,
    /// The General Configuration Register, at offset 0x10.
    pub general_configuration:       Volatile<u64>,
    _padding1:                       u64,
    /// The General Interrupt Status Register, at offset 0x20.
    pub general_interrupt_status:    Volatile<u64>,
    _padding2:                       [u64; (0xF0 - 0x28) / 8], // 25 u64s
    /// The Main Counter Value Register, at offset 0xF0.
    pub main_counter_value:          Volatile<u64>,
    _padding3:                       u64,
    /// The timers (comparators) available for separate.
    /// There is a minimum of 3 timers and a maximum of 32 in an HPET-enabled system.
    /// Call [`num_timers`](#method.num_timers) to get the actual number of HPET timers.
    pub timers:                      [HpetTimer; 32],
}
const _: () = assert!(core::mem::size_of::<Hpet>() == 1280);

impl Hpet {
    /// Returns the HPET's main counter value
    pub fn get_counter(&self) -> u64 {
        self.main_counter_value.read()
    }

    /// Turns on or off the main counter
    pub fn enable_counter(&mut self, enable: bool) {
        if enable {
            // set bit 0
            self.general_configuration.update(|old_val_ref| *old_val_ref |= 0x1);
        }
        else {
            // clear bit 0
            self.general_configuration.update(|old_val_ref| *old_val_ref &= !0x1);
        }
            
    }

    /// Returns the period of the HPET counter in femtoseconds,
    /// i.e., the length of time that one HPET tick takes.
    /// 
    /// Can be used to calculate the frequency of the HPET clock.
    /// 
    /// Must not be zero, must be less or equal to 0x05F5E100 (100 nanoseconds)
    pub fn counter_period_femtoseconds(&self) -> u32 {
        let caps = self.general_capabilities_and_id.read();
        let period = caps >> 32;
        period as u32
    }

    pub fn vendor_id(&self) -> u16 {
        let caps = self.general_capabilities_and_id.read();
        let id = caps >> 16;
        id as u16
    }

    pub fn num_timers(&self) -> u8 {
        let caps = self.general_capabilities_and_id.read();
        // only bits [12:8] matter
        let count = (caps >> 8) & 0b11111; // only 5 bits matter
        // that gives us the number of timers minus one, so add one back to it
        (count + 1) as u8
    }
}

impl time::ClockSource for Hpet {
    type ClockType = time::Monotonic;

    fn now() -> Instant {
        Instant::new(get_hpet().expect("couldn't get HPET").get_counter())
    }
}

/// A structure that wraps HPET I/O register for each timer comparator, 
/// specified by the format here: <https://wiki.osdev.org/HPET#HPET_registers>.
/// There are between 3 and 32 of these in an HPET-enabled system.
#[derive(FromBytes)]
#[repr(C)]
pub struct HpetTimer {
    /// This timer's Configuration and Capability register.
    pub configuration_and_capability: Volatile<u64>,
    /// This timer's Comparator Value register.
    pub comparator_value:             Volatile<u64>,
    /// This timer's FSB Interrupt Route register.
    /// Some info here: <https://wiki.osdev.org/HPET#FSB_mapping>
    pub fsb_interrupt_route:          Volatile<u64>,
    _padding:                         u64,
}
const _: () = assert!(core::mem::size_of::<HpetTimer>() == 32);


pub const HPET_SIGNATURE: &[u8; 4] = b"HPET";

/// The handler for parsing the HPET table and adding it to the ACPI tables list.
pub fn handle(
    acpi_tables: &mut AcpiTables,
    signature: AcpiSignature,
    _length: usize,
    phys_addr: PhysicalAddress
) -> Result<(), &'static str> {
    acpi_tables.add_table_location(signature, phys_addr, None)
}

/// The structure of the HPET ACPI table.
#[derive(Clone, Copy, Debug, FromBytes)]
#[repr(C, packed)]
pub struct HpetAcpiTable {
    header: Sdt,
    _hardware_revision_id: u8,
    _comparator_descriptor: u8,
    _pci_vendor_id: u16,
    gen_addr_struct: GenericAddressStructure,
    _hpet_number: u8,
    _min_periodic_clock_tick: u16,
    /// also called 'page_protection'
    _oem_attribute: u8,
}
const _: () = assert!(core::mem::size_of::<HpetAcpiTable>() == 56);

impl HpetAcpiTable {
    /// Finds the HPET in the given `AcpiTables` and returns a reference to it.
    pub fn get(acpi_tables: &AcpiTables) -> Option<&HpetAcpiTable> {
        acpi_tables.table(HPET_SIGNATURE).ok()
    }

    /// Initializes the HPET counter-based timer
    /// based on the hardware details from this ACPI table.
    /// 
    /// Returns a reference to the initialized `Hpet` structure.
    pub fn init_hpet(
        &self,
        page_table: &mut PageTable,
    ) -> Result<&'static RwLock<BorrowedMappedPages<Hpet, Mutable>>, &'static str> {
        let phys_addr = PhysicalAddress::new(self.gen_addr_struct.phys_addr as usize)
            .ok_or("HPET physical address was invalid")?;
        let frames = allocate_frames_by_bytes_at(phys_addr, self.header.length as usize)
            .map_err(|_e| "Couldn't allocate frames for HPET")?;
        let pages = allocate_pages(frames.size_in_frames())
            .ok_or("Couldn't allocate pages for HPET")?;
        let hpet_mp = page_table.map_allocated_pages_to(
            pages,
            frames,
            PteFlags::new().valid(true).writable(true).device_memory(true),
        )?;

        let mut hpet = hpet_mp.into_borrowed_mut::<Hpet>(phys_addr.frame_offset())
            .map_err(|(|_mp, s)| s)?;
        // enable the main counter
        {
            hpet.enable_counter(true);
            debug!("Initialized HPET, period: {}, counter val: {}, num timers: {}, vendor_id: {}", 
                hpet.counter_period_femtoseconds(), hpet.get_counter(), hpet.num_timers(), hpet.vendor_id()
            );
        }

        Ok(HPET.call_once(|| RwLock::new(hpet)))
    }
}