aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2017-11-02 11:21:14 -0400
committerKevin O'Connor <kevin@koconnor.net>2017-11-02 11:28:25 -0400
commit488e1c3fef2e789d38a5190baae977131a911fa7 (patch)
tree46846492609a01d9b86f3dddcf478e808c2e1ea4
parentcd47172a673762a05a0c7bd27df6e3cc8febe8d6 (diff)
downloadseabios-488e1c3fef2e789d38a5190baae977131a911fa7.zip
seabios-488e1c3fef2e789d38a5190baae977131a911fa7.tar.gz
seabios-488e1c3fef2e789d38a5190baae977131a911fa7.tar.bz2
timer: Avoid integer overflows in usec and nsec calculations
When timer_calc_usec() is used with large timeout values, such as 60s, the integer math can overflow and produce different results than when using timer_calc(time / 1000) for the same timeout. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--src/hw/timer.c55
1 files changed, 31 insertions, 24 deletions
diff --git a/src/hw/timer.c b/src/hw/timer.c
index 03d22b2..bdcb3bf 100644
--- a/src/hw/timer.c
+++ b/src/hw/timer.c
@@ -159,6 +159,29 @@ timer_read(void)
return timer_adjust_bits(v, 0xffff);
}
+// Return the TSC value that is 'msecs' time in the future.
+u32
+timer_calc(u32 msecs)
+{
+ return timer_read() + (GET_GLOBAL(TimerKHz) * msecs);
+}
+u32
+timer_calc_usec(u32 usecs)
+{
+ u32 cur = timer_read(), khz = GET_GLOBAL(TimerKHz);
+ if (usecs > 500000)
+ return cur + DIV_ROUND_UP(usecs, 1000) * khz;
+ return cur + DIV_ROUND_UP(usecs * khz, 1000);
+}
+static u32
+timer_calc_nsec(u32 nsecs)
+{
+ u32 cur = timer_read(), khz = GET_GLOBAL(TimerKHz);
+ if (nsecs > 500000)
+ return cur + DIV_ROUND_UP(nsecs, 1000000) * khz;
+ return cur + DIV_ROUND_UP(nsecs * khz, 1000000);
+}
+
// Check if the current time is past a previously calculated end time.
int
timer_check(u32 end)
@@ -167,53 +190,37 @@ timer_check(u32 end)
}
static void
-timer_delay(u32 diff)
+timer_delay(u32 end)
{
- u32 start = timer_read();
- u32 end = start + diff;
while (!timer_check(end))
cpu_relax();
}
static void
-timer_sleep(u32 diff)
+timer_sleep(u32 end)
{
- u32 start = timer_read();
- u32 end = start + diff;
while (!timer_check(end))
yield();
}
void ndelay(u32 count) {
- timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000));
+ timer_delay(timer_calc_nsec(count));
}
void udelay(u32 count) {
- timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000));
+ timer_delay(timer_calc_usec(count));
}
void mdelay(u32 count) {
- timer_delay(count * GET_GLOBAL(TimerKHz));
+ timer_delay(timer_calc(count));
}
void nsleep(u32 count) {
- timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000));
+ timer_sleep(timer_calc_nsec(count));
}
void usleep(u32 count) {
- timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000));
+ timer_sleep(timer_calc_usec(count));
}
void msleep(u32 count) {
- timer_sleep(count * GET_GLOBAL(TimerKHz));
-}
-
-// Return the TSC value that is 'msecs' time in the future.
-u32
-timer_calc(u32 msecs)
-{
- return timer_read() + (GET_GLOBAL(TimerKHz) * msecs);
-}
-u32
-timer_calc_usec(u32 usecs)
-{
- return timer_read() + DIV_ROUND_UP(GET_GLOBAL(TimerKHz) * usecs, 1000);
+ timer_sleep(timer_calc(count));
}