PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
hpet.c
Go to the documentation of this file.
1#include <kernel/cpu/cpu.h>
2#include <kernel/log/log.h>
3#include <kernel/log/panic.h>
4#include <kernel/mem/vmm.h>
10#include <kernel/sched/wait.h>
11#include <kernel/sync/seqlock.h>
12#include <kernel/utils/utils.h>
13#include <kernel/acpi/tables.h>
14
15/**
16 * @brief High Precision Event Timer
17 * @defgroup kernel_drivers_hpet HPET
18 * @ingroup kernel_drivers
19 *
20 * @note Since the HPET might be 32bit it could overflow rather quickly, so we implement a system for checking roughly
21 * when it will overflow and accumulate the counter into a 64 bit nanosecond counter.
22 *
23 * @see [OSDev HPET](https://wiki.osdev.org/HPET)
24 *
25 * @{
26 */
27
28/**
29 * @brief HPET register offsets
30 * @enum hpet_register_t
31 */
41
42/**
43 * @brief The bit offset of the clock period in the capabilities register
44 */
45#define HPET_CAP_COUNTER_CLK_PERIOD_SHIFT 32
46
47/**
48 * @brief The bit to set to enable the HPET in the configuration register
49 */
50#define HPET_CONF_ENABLE_CNF_BIT (1 << 0)
51
52/**
53 * @brief The bit to set to enable legacy replacement mode in the configuration register
54 */
55#define HPET_CONF_LEG_RT_CNF_BIT (1 << 1)
56
57/**
58 * @brief If `hpet_t::addressSpaceId` is equal to this, the address is in system memory space.
59 */
60#define HPET_ADDRESS_SPACE_MEMORY 0
61
62/**
63 * @brief If `hpet_t::addressSpaceId` is equal to this, the address is in system I/O space.
64 */
65#define HPET_ADDRESS_SPACE_IO 1
66
67/**
68 * @brief The number of femtoseconds in one second
69 */
70#define HPET_FEMTOSECONDS_PER_SECOND 1000000000000000ULL
71
72/**
73 * @brief High Precision Event Timer structure
74 * @struct hpet_t
75 */
94
95static hpet_t* hpet; ///< Pointer to the HPET ACPI table.
96static uintptr_t address; ///< Mapped virtual address of the HPET registers.
97static uint64_t period; ///< Main counter tick period in femtoseconds (10^-15 s).
98
99static atomic_uint64_t counter = ATOMIC_VAR_INIT(0); ///< Accumulated nanosecond counter, used to avoid overflows.
100static seqlock_t counterLock = SEQLOCK_CREATE(); ///< Seqlock for the accumulated counter.
101
102static tid_t overflowThreadTid = 0; ///< Thread ID of the overflow thread.
103static wait_queue_t overflowQueue = WAIT_QUEUE_CREATE(overflowQueue); ///< Wait queue for the overflow thread.
104static atomic_bool overflowShouldStop = ATOMIC_VAR_INIT(false); ///< Flag to signal the overflow thread to stop.
105
106/**
107 * @brief Write to an HPET register.
108 *
109 * @param reg The register to write to.
110 * @param value The value to write.
111 */
112static inline void hpet_write(hpet_register_t reg, uint64_t value)
113{
114 WRITE_64(address + reg, value);
115}
116
117/**
118 * @brief Read from an HPET register.
119 *
120 * @param reg The register to read from.
121 * @return The value read from the register.
122 */
124{
125 return READ_64(address + reg);
126}
127
128/**
129 * @brief Get the HPET clock period in nanoseconds.
130 *
131 * @return The HPET clock period in nanoseconds.
132 */
133static inline clock_t hpet_ns_per_tick(void)
134{
136}
137
138/**
139 * @brief Safely read the HPET counter value in nanoseconds.
140 *
141 * @return The current value of the HPET counter in nanoseconds.
142 */
144{
146 uint64_t seq;
147 do
148 {
151 } while (seqlock_read_retry(&counterLock, seq));
152 return time;
153}
154
155/**
156 * @brief Reset the HPET main counter to zero and enable the HPET.
157 */
164
165/**
166 * @brief Thread function that periodically accumulates the HPET counter to prevent overflow.
167 *
168 * @param arg Unused.
169 */
170static void hpet_overflow_thread(void* arg)
171{
172 UNUSED(arg);
173
174 // Assume the worst case where the HPET is 32bit, since `clock_t` isent large enough to hold the time otherwise and
175 // i feel paranoid.
176 clock_t sleepInterval = (UINT32_MAX * hpet_ns_per_tick()) / 2;
177 LOG_INFO("HPET overflow thread started, sleep interval %lluns\n", sleepInterval);
178
180 {
181 WAIT_BLOCK_TIMEOUT(&overflowQueue, false, sleepInterval);
182
187 }
188}
189
190/**
191 * @brief Structure to describe the HPET to the sys time subsystem.
192 */
194 .name = "HPET",
195 .precision = 0, // Filled in during init
196 .read_ns = hpet_read_ns_counter,
197 .read_epoch = NULL,
198};
199
200/**
201 * @brief Initialize the HPET.
202 *
203 * @return On success, `0`. On failure, `ERR`.
204 */
205static uint64_t hpet_init(void)
206{
207 hpet = (hpet_t*)acpi_tables_lookup("HPET", sizeof(hpet_t), 0);
208 if (hpet == NULL)
209 {
210 LOG_ERR("failed to locate HPET table\n");
211 return ERR;
212 }
213
215 {
216 LOG_ERR("HPET address space is not memory (id=%u) which is not supported\n", hpet->addressSpaceId);
217 return ERR;
218 }
219
222 NULL)
223 {
224 LOG_ERR("failed to map HPET memory at %p\n", hpet->address);
225 return ERR;
226 }
227
230
231 if (period == 0 || period >= 0x05F5E100)
232 {
233 LOG_ERR("HPET reported an invalid counter period %llu fs\n", period);
234 return ERR;
235 }
236
237 LOG_INFO("started HPET timer phys=%p virt=%p period=%lluns timers=%u %s-bit\n", hpet->address, address,
239 hpet->counterIs64Bit ? "64" : "32");
240
242
245 {
246 LOG_ERR("failed to register HPET as system time source\n");
247 return ERR;
248 }
249
251 if (overflowThreadTid == ERR)
252 {
253 LOG_ERR("failed to create HPET overflow thread\n");
255 return ERR;
256 }
257
258 return 0;
259}
260
261/**
262 * @brief Deinitialize the HPET.
263 */
277
278/** @} */
279
281{
282 switch (event->type)
283 {
285 if (hpet_init() == ERR)
286 {
287 panic(NULL, "Failed to initialize HPET module");
288 }
289 break;
291 hpet_deinit();
292 break;
293 default:
294 break;
295 }
296
297 return 0;
298}
299
300MODULE_INFO("HPET Driver", "Kai Norberg", "A High Precision Event Timer driver", OS_VERSION, "MIT", "PNP0103");
#define CLOCKS_PER_SEC
Definition clock_t.h:15
sdt_header_t * acpi_tables_lookup(const char *signature, uint64_t minSize, uint64_t n)
Lookup the n'th table matching the signature.
Definition tables.c:259
static uint64_t hpet_init(void)
Initialize the HPET.
Definition hpet.c:205
static clock_t hpet_read_ns_counter(void)
Safely read the HPET counter value in nanoseconds.
Definition hpet.c:143
#define HPET_CONF_ENABLE_CNF_BIT
The bit to set to enable the HPET in the configuration register.
Definition hpet.c:50
static atomic_uint64_t counter
Accumulated nanosecond counter, used to avoid overflows.
Definition hpet.c:99
static uint64_t period
Main counter tick period in femtoseconds (10^-15 s).
Definition hpet.c:97
static void hpet_write(hpet_register_t reg, uint64_t value)
Write to an HPET register.
Definition hpet.c:112
hpet_register_t
HPET register offsets.
Definition hpet.c:33
static hpet_t * hpet
Pointer to the HPET ACPI table.
Definition hpet.c:95
static wait_queue_t overflowQueue
Wait queue for the overflow thread.
Definition hpet.c:103
#define HPET_FEMTOSECONDS_PER_SECOND
The number of femtoseconds in one second.
Definition hpet.c:70
#define HPET_CAP_COUNTER_CLK_PERIOD_SHIFT
The bit offset of the clock period in the capabilities register.
Definition hpet.c:45
static atomic_bool overflowShouldStop
Flag to signal the overflow thread to stop.
Definition hpet.c:104
static clock_t hpet_ns_per_tick(void)
Get the HPET clock period in nanoseconds.
Definition hpet.c:133
static void hpet_overflow_thread(void *arg)
Thread function that periodically accumulates the HPET counter to prevent overflow.
Definition hpet.c:170
static void hpet_reset_counter(void)
Reset the HPET main counter to zero and enable the HPET.
Definition hpet.c:158
static clock_source_t source
Structure to describe the HPET to the sys time subsystem.
Definition hpet.c:193
static void hpet_deinit(void)
Deinitialize the HPET.
Definition hpet.c:264
static tid_t overflowThreadTid
Thread ID of the overflow thread.
Definition hpet.c:102
static uint64_t hpet_read(hpet_register_t reg)
Read from an HPET register.
Definition hpet.c:123
static seqlock_t counterLock
Seqlock for the accumulated counter.
Definition hpet.c:100
static uintptr_t address
Mapped virtual address of the HPET registers.
Definition hpet.c:96
#define HPET_ADDRESS_SPACE_MEMORY
If hpet_t::addressSpaceId is equal to this, the address is in system memory space.
Definition hpet.c:60
@ HPET_REG_GENERAL_CONFIG
Definition hpet.c:35
@ HPET_REG_GENERAL_CAPABILITIES_ID
Definition hpet.c:34
@ HPET_REG_MAIN_COUNTER_VALUE
Definition hpet.c:37
@ HPET_REG_GENERAL_INTERRUPT
Definition hpet.c:36
@ HPET_REG_TIMER0_COMPARATOR
Definition hpet.c:39
@ HPET_REG_TIMER0_CONFIG_CAP
Definition hpet.c:38
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOG_ERR(format,...)
Definition log.h:93
#define LOG_INFO(format,...)
Definition log.h:91
#define PML_LOWER_TO_HIGHER(addr)
Converts an address from the lower half to the higher half.
@ PML_PRESENT
@ PML_WRITE
@ PML_GLOBAL
void * vmm_map(space_t *space, void *virtAddr, phys_addr_t physAddr, size_t length, pml_flags_t flags, space_callback_func_t func, void *data)
Maps physical memory to virtual memory in a given address space.
Definition vmm.c:226
#define MODULE_INFO(_name, _author, _description, _version, _licence, _deviceTypes)
Macro to define module information.
Definition module.h:200
@ MODULE_EVENT_DEVICE_ATTACH
Definition module.h:251
@ MODULE_EVENT_DEVICE_DETACH
Definition module.h:257
process_t * process_get_kernel(void)
Gets the kernel process.
Definition process.c:374
bool process_has_thread(process_t *process, tid_t tid)
Checks if a process has a thread with the specified thread ID.
Definition process.c:358
uint64_t clock_source_register(const clock_source_t *source)
Register a system timer source.
Definition clock.c:47
void clock_source_unregister(const clock_source_t *source)
Unregister a system timer source.
Definition clock.c:72
tid_t thread_kernel_create(thread_kernel_entry_t entry, void *arg)
Creates a new thread that runs in kernel mode and submits it to the scheduler.
Definition thread.c:126
uint64_t wait_unblock(wait_queue_t *queue, uint64_t amount, errno_t err)
Unblock threads waiting on a wait queue.
Definition wait.c:307
#define WAIT_ALL
Used to indicate that the wait should unblock all waiting threads.
Definition wait.h:43
#define WAIT_BLOCK_TIMEOUT(queue, condition, timeout)
Blocks until the condition is true, condition will be tested on every wakeup. Reaching the timeout wi...
Definition wait.h:75
#define WAIT_QUEUE_CREATE(name)
Create a wait queue initializer.
Definition wait.h:222
void sched_yield(void)
Yield the current thread's time slice to allow other threads to run.
Definition sched.c:633
static uint64_t seqlock_read_begin(seqlock_t *seqlock)
Begins a read operation on a sequence lock.
Definition seqlock.h:98
static void seqlock_write_release(seqlock_t *seqlock)
Releases the write lock of a sequence lock.
Definition seqlock.h:75
static bool seqlock_read_retry(seqlock_t *seqlock, uint64_t seq)
Checks if a read operation on a sequence lock needs to be retried.
Definition seqlock.h:110
#define SEQLOCK_CREATE()
Create a sequence lock initializer.
Definition seqlock.h:40
static void seqlock_write_acquire(seqlock_t *seqlock)
Acquires the write lock of a sequence lock.
Definition seqlock.h:64
#define EOK
No error.
Definition errno.h:32
#define PACKED
GCC packed attribute.
Definition defs.h:33
#define UNUSED(x)
Mark a variable as unused.
Definition defs.h:96
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define PAGE_SIZE
The size of a memory page in bytes.
Definition PAGE_SIZE.h:8
__UINT64_TYPE__ tid_t
Thread Identifier.
Definition tid_t.h:12
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
uint64_t _module_procedure(const module_event_t *event)
Definition hpet.c:280
#define atomic_store(object, desired)
Definition stdatomic.h:289
#define atomic_load(object)
Definition stdatomic.h:288
#define ATOMIC_VAR_INIT(value)
Definition stdatomic.h:74
#define atomic_fetch_add(object, operand)
Definition stdatomic.h:283
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
__UINT16_TYPE__ uint16_t
Definition stdint.h:13
#define UINT32_MAX
Definition stdint.h:70
Clock source structure.
Definition clock.h:35
const char * name
Definition clock.h:36
clock_t precision
Definition clock.h:37
High Precision Event Timer structure.
Definition hpet.c:77
uint8_t hardwareRevId
Definition hpet.c:79
uint8_t legacyReplacementCapable
Definition hpet.c:83
uint8_t reserved1
Definition hpet.c:82
uint16_t pciVendorId
Definition hpet.c:84
uint8_t registerBitWidth
Definition hpet.c:86
uint64_t address
Definition hpet.c:89
uint8_t hpetNumber
Definition hpet.c:90
uint8_t addressSpaceId
Definition hpet.c:85
sdt_header_t header
Definition hpet.c:78
uint8_t counterIs64Bit
Definition hpet.c:81
uint8_t pageProtection
Definition hpet.c:92
uint16_t minimumTick
Definition hpet.c:91
uint8_t registerBitOffset
Definition hpet.c:87
uint8_t comparatorCount
Definition hpet.c:80
uint8_t reserved2
Definition hpet.c:88
module_event_type_t type
Definition module.h:268
System Description Table Header.
Definition acpi.h:90
Sequence lock structure.
Definition seqlock.h:30
The primitive that threads block on.
Definition wait.h:185
_PUBLIC time_t time(time_t *timer)
Definition time.c:5
#define WRITE_64(address, value)
Definition utils.h:19
#define READ_64(address)
Definition utils.h:18