CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-timer.hh
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <cheri.hh>
7#include <stdint.h>
8#include <utils.hh>
9
10/**
11 * Driver for using the (old) RISC-V Core-Local Interrupt (CLINT)
12 * controller (as opposed to the newer CLIC) to provide timer signals.
13 * Note that CLIC can run in CLINT-compatible mode, so we don't need to
14 * support CLIC for a while, it has a lot of features that are overkill for
15 * most embedded devices.
16 */
18{
19 public:
20 /**
21 * Initialise the interface.
22 */
23 static void init()
24 {
25 // This needs to be csr_read(mhartid) if we support multicore
26 // systems, but we don't plan to any time soon.
27 constexpr uint32_t HartId = 0;
28
29 auto setField = [&](auto &field, size_t offset, size_t size) {
30 CHERI::Capability capability{MMIO_CAPABILITY(uint32_t, clint)};
31 capability.address() += offset;
32 capability.bounds() = size;
33 field = capability;
34 };
35 setField(pmtimer, StandardClint::ClintMtime, 2 * sizeof(uint32_t));
36 setField(pmtimercmp,
37 StandardClint::ClintMtimecmp + HartId * 8,
38 2 * sizeof(uint32_t));
39 }
40
41 static uint64_t time()
42 {
43 // The timer is little endian, so the high 32 bits are after the low 32
44 // bits. We can't do atomic 64-bit loads and so we have to read these
45 // separately.
46 volatile uint32_t *timerHigh = pmtimer + 1;
47 uint32_t timeLow, timeHigh;
48
49 // Read the current time. Loop until the high 32 bits are stable.
50 do
51 {
52 timeHigh = *timerHigh;
53 timeLow = *pmtimer;
54 } while (timeHigh != *timerHigh);
55 return (static_cast<uint64_t>(timeHigh) << 32) | timeLow;
56 }
57
58 /**
59 * Set the timer comparison for next interrupt to given value.
60 * Note that CLINT does not need any action to acknowledge the
61 * interrupt. Writing to it is enough to clear the interrupt.
62 * Interrupts must be disabled when calling this function.
63 */
64 static void setnext(uint64_t nextTime)
65 {
66 /// the high 32 bits of the 64-bit MTIME register
67 volatile uint32_t *pmtimercmphigh = pmtimercmp + 1;
68
69 // Attempt to prevent spurious interrupts by writing -1 to mtimeh. The
70 // RISC-V spec actually says to do this by writing to the low 32-bits,
71 // but writing -1 to the high bits also works if we assume that mtimeh
72 // will never reach its maximum value, which it had better not because
73 // the >= comparison makes it impossible to handle wrap around of the
74 // mtime counter anyway. This may be unnecessary given that that we
75 // should have interrupts disabled anyway...
76 *pmtimercmphigh = -1;
77 // Write the new MTIMECMP value, at which the next interrupt fires.
78 *pmtimercmp = nextTime;
79 *pmtimercmphigh = nextTime >> 32;
80 }
81
82 /**
83 * Returns the time at which the next timer is scheduled.
84 */
85 static uint64_t next()
86 {
87 volatile uint32_t *pmtimercmphigh = pmtimercmp + 1;
88 uint64_t nextTimer = *pmtimercmphigh;
89 nextTimer <<= 32;
90 nextTimer |= *pmtimercmp;
91 return nextTimer;
92 }
93
94 /**
95 * Set the timer comparator to a value that should never trigger an
96 * interrupt.
97 *
98 * Note: we rely on the fact that mtimeh should never reach maximum value in
99 * plausible time-frame.
100 */
101 static void clear()
102 {
103 volatile uint32_t *pmtimercmphigh = pmtimercmp + 1;
104 *pmtimercmphigh = -1; // Prevent spurious interrupts.
105 }
106
107 private:
108#ifdef MICROSOFT_CHERIOT_SAFE
109 /**
110 * The Microsoft SAFE platform doesn't implement a complete CLINT, only the
111 * timer part (which is all that we use). Its offsets within the CLINT
112 * region are different.
113 */
114 static constexpr size_t ClintMtime = 0x10;
115 static constexpr size_t ClintMtimecmp = 0x18;
116#else
117 /**
118 * The standard RISC-V CLINT is a large memory-mapped device and places the
119 * timers a long way in.
120 */
121 static constexpr size_t ClintMtimecmp = 0x4000U;
122 static constexpr size_t ClintMtime = 0xbff8U;
123#endif
124
125 static inline volatile uint32_t *pmtimercmp;
126 static inline volatile uint32_t *pmtimer;
127};
128
129using TimerCore = StandardClint;
C++ helpers for operating on capabilities.
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
AddressProxy address()
Access the address of the capability.
Definition cheri.hh:851
BoundsProxy bounds()
Access (read, set) the capability's bounds.
Definition cheri.hh:867
Driver for using the (old) RISC-V Core-Local Interrupt (CLINT) controller (as opposed to the newer CL...
static uint64_t next()
Returns the time at which the next timer is scheduled.
static void init()
Initialise the interface.
static void clear()
Set the timer comparator to a value that should never trigger an interrupt.
static void setnext(uint64_t nextTime)
Set the timer comparison for next interrupt to given value.
Utility class to delete copy and move contructors.
Definition utils.hh:53
#define MMIO_CAPABILITY(type, name)
Provide a capability of the type volatile type * referring to the MMIO region exported in the linker ...
Miscellaneous utility functions and classes.