Skip to content

Commit 5f63c9d

Browse files
Andy Rosscarlescufi
authored andcommitted
drivers/timer: Clamp after tick adjustment, not before
Some early tickless drivers had a common pattern where they would compute a tick maximum for the request (i.e. the maximum the hardware counter can handle) but apply it only on the input tick value and not on the adjusted final value, opening up the overflow condition it was supposed to have prevented. Fixes #20939 (Strictly it fixes the specific pattern that was discovered in that bug. It's not impossible that other drivers with alternative implementations have a similar issue, though they look OK to me via a quick audit). Signed-off-by: Andy Ross <[email protected]>
1 parent 44bbdd0 commit 5f63c9d

File tree

4 files changed

+35
-16
lines changed

4 files changed

+35
-16
lines changed

drivers/timer/cc13x2_cc26x2_rtc_timer.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
/*
4040
* Maximum number of ticks.
4141
*/
42-
#define MAX_TICKS (0x7FFFFFFFFFFFULL / RTC_COUNTS_PER_TICK)
42+
#define MAX_CYC 0x7FFFFFFFFFFFULL
43+
#define MAX_TICKS (MAX_CYC / RTC_COUNTS_PER_TICK)
4344

4445
/*
4546
* Due to the nature of clock synchronization, the comparator cannot be set
@@ -217,7 +218,7 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
217218
/* Round to the nearest tick boundary. */
218219
timeout = (timeout + RTC_COUNTS_PER_TICK - 1) / RTC_COUNTS_PER_TICK
219220
* RTC_COUNTS_PER_TICK;
220-
221+
timeout = MIN(timeout, MAX_CYC);
221222
timeout += rtc_last;
222223

223224
/* Set the comparator */

drivers/timer/hpet.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,17 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
136136
ticks = MAX(MIN(ticks - 1, (s32_t)max_ticks), 0);
137137

138138
k_spinlock_key_t key = k_spin_lock(&lock);
139-
u32_t now = MAIN_COUNTER_REG, cyc;
140-
141-
/* Round up to next tick boundary */
142-
cyc = ticks * cyc_per_tick + (now - last_count) + (cyc_per_tick - 1);
139+
u32_t now = MAIN_COUNTER_REG, cyc, adj;
140+
u32_t max_cyc = max_ticks * cyc_per_tick;
141+
142+
/* Round up to next tick boundary. */
143+
cyc = ticks * cyc_per_tick;
144+
adj = (now - last_count) + (cyc_per_tick - 1);
145+
if (cyc <= max_cyc - adj) {
146+
cyc += adj;
147+
} else {
148+
cyc = max_cyc;
149+
}
143150
cyc = (cyc / cyc_per_tick) * cyc_per_tick;
144151
cyc += last_count;
145152

drivers/timer/riscv_machine_timer.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
#define CYC_PER_TICK ((u32_t)((u64_t)sys_clock_hw_cycles_per_sec() \
1212
/ (u64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
13-
#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK)
13+
#define MAX_CYC 0xffffffffu
14+
#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
1415
#define MIN_DELAY 1000
1516

1617
#define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && \
@@ -99,12 +100,15 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
99100

100101
k_spinlock_key_t key = k_spin_lock(&lock);
101102
u64_t now = mtime();
102-
u32_t cyc = ticks * CYC_PER_TICK;
103-
104-
/* Round up to next tick boundary. Note use of 32 bit math,
105-
* max_ticks is calibrated to permit this.
106-
*/
107-
cyc += (u32_t)(now - last_count) + (CYC_PER_TICK - 1);
103+
u32_t adj, cyc = ticks * CYC_PER_TICK;
104+
105+
/* Round up to next tick boundary. */
106+
adj = (u32_t)(now - last_count) + (CYC_PER_TICK - 1);
107+
if (cyc <= MAX_CYC - adj) {
108+
cyc += adj;
109+
} else {
110+
cyc = MAX_CYC;
111+
}
108112
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
109113

110114
if ((s32_t)(cyc + last_count - now) < MIN_DELAY) {

drivers/timer/xtensa_sys_timer.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313

1414
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \
1515
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC)
16-
#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK)
16+
#define MAX_CYC 0xffffffffu
17+
#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
1718
#define MIN_DELAY 1000
1819

1920
static struct k_spinlock lock;
@@ -74,10 +75,16 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
7475
ticks = MAX(MIN(ticks - 1, (s32_t)MAX_TICKS), 0);
7576

7677
k_spinlock_key_t key = k_spin_lock(&lock);
77-
u32_t curr = ccount(), cyc;
78+
u32_t curr = ccount(), cyc, adj;
7879

7980
/* Round up to next tick boundary */
80-
cyc = ticks * CYC_PER_TICK + (curr - last_count) + (CYC_PER_TICK - 1);
81+
cyc = ticks * CYC_PER_TICK;
82+
adj = (curr - last_count) + (CYC_PER_TICK - 1);
83+
if (cyc <= MAX_CYC - adj) {
84+
cyc += adj;
85+
} else {
86+
cyc = MAX_CYC;
87+
}
8188
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
8289
cyc += last_count;
8390

0 commit comments

Comments
 (0)