summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-11-29 10:44:30 -0800
committerRandall Spangler <rspangler@chromium.org>2012-11-29 12:06:55 -0800
commitad2d36dbd4bfa4043931108b287d9a49df1965ca (patch)
treef0a4ccdc351daa56148db8bef9f050db5fb79a2d
parent9444f1edb88d2ed3e9d185f14e32401c6eebc5dd (diff)
downloadchrome-ec-ad2d36dbd4bfa4043931108b287d9a49df1965ca.tar.gz
Fix potential deadlock in udelay()
If interrupts are disabled and the deadline is across a 32-bit timer boundary from the current time, udelay() can lock up. The fix is to do 32-bit math directly in udelay(). BUG=chrome-os-partner:16472 BRANCH=link TEST=manual waitms 1 -> prompt returns almost instantly waitms 500 -> prompt returns after 0.5 sec waitms 1000 -> watchdog error printed, then prompt returns waitms 2000 -> watchdog reboot Original-Change-Id: Ib8ca06cee414d48900c0142e629daa68aa0993c9 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/38924 Reviewed-by: Yung-Chieh Lo <yjlou@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> (cherry picked from commit 108754005d86c17b6247e83e2b7aed788d93a8c9) Change-Id: Id5ad899488a77bdd923a0169280b3e81bc6714ea Reviewed-on: https://gerrit.chromium.org/gerrit/38933 Reviewed-by: Randall Spangler <rspangler@chromium.org> Tested-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--core/cortex-m/timer.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/core/cortex-m/timer.c b/core/cortex-m/timer.c
index 994f9c1245..ea97fc3a66 100644
--- a/core/cortex-m/timer.c
+++ b/core/cortex-m/timer.c
@@ -96,10 +96,20 @@ void process_timers(int overflow)
void udelay(unsigned us)
{
- timestamp_t deadline = get_time();
-
- deadline.val += us;
- while (get_time().val < deadline.val) {}
+ unsigned t0 = __hw_clock_source_read();
+
+ /*
+ * udelay() may be called with interrupts disabled, so we can't rely on
+ * process_timers() updating the top 32 bits. So handle wraparound
+ * ourselves rather than calling get_time() and comparing with a
+ * deadline.
+ *
+ * This may fail for delays close to 2^32 us (~4000 sec), because the
+ * subtraction below can overflow. That's acceptable, because the
+ * watchdog timer would have tripped long before that anyway.
+ */
+ while (__hw_clock_source_read() - t0 < us)
+ ;
}