Work around very broken timers.

Submitted by Stefan Tauner on July 30, 2016, 7:56 p.m.

Details

Message ID 20160730195653.11008-1-stefan.tauner@alumni.tuwien.ac.at
State New
Headers show

Commit Message

Stefan Tauner July 30, 2016, 7:56 p.m.
Allow myusec_calibrate_delay() to indicate errors and abort
in cli_classic if that happens.

Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
---
 cli_classic.c |  6 +++++-
 programmer.h  |  2 +-
 udelay.c      | 22 ++++++++++++++--------
 3 files changed, 20 insertions(+), 10 deletions(-)
 
Roxfan on IRC had major problems getting flashrom to even start because
of some hardware-related timing problems. Without this patch his hardware
would (almost?) endlessly loop during timer calibration. This patch
aborts instead after a while and should not trigger any regressions.

One can test it by overriding the gettimeofday results in measure_delay()
by adding following assignments:
	start.tv_sec = 0;
	end.tv_sec = 0;
	start.tv_usec = 0;
	end.tv_usec = 0;

Patch hide | download patch | download mbox

diff --git a/cli_classic.c b/cli_classic.c
index a2c2014..4a68f0b 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -414,7 +414,11 @@  int main(int argc, char *argv[])
 	}
 
 	/* FIXME: Delay calibration should happen in programmer code. */
-	myusec_calibrate_delay();
+	if (myusec_calibrate_delay() != 0) {
+		msg_perr("Error: Custom timer calibration failed.\n");
+		ret = 1;
+		goto out_shutdown;
+	}
 
 	if (programmer_init(prog, pparam)) {
 		msg_perr("Error: Programmer initialization failed.\n");
diff --git a/programmer.h b/programmer.h
index bd8e98d..b39fba4 100644
--- a/programmer.h
+++ b/programmer.h
@@ -268,8 +268,8 @@  extern const struct board_info laptops_known[];
 #endif
 
 /* udelay.c */
+int myusec_calibrate_delay(void);
 void myusec_delay(unsigned int usecs);
-void myusec_calibrate_delay(void);
 void internal_sleep(unsigned int usecs);
 void internal_delay(unsigned int usecs);
 
diff --git a/udelay.c b/udelay.c
index 7c6961d..3562268 100644
--- a/udelay.c
+++ b/udelay.c
@@ -85,7 +85,7 @@  static unsigned long measure_delay(unsigned int usecs)
 	return timeusec;
 }
 
-void myusec_calibrate_delay(void)
+int myusec_calibrate_delay(void)
 {
 	unsigned long count = 1000;
 	unsigned long timeusec, resolution;
@@ -106,16 +106,21 @@  recalibrate:
 		if (timeusec > 1000000 / 4)
 			break;
 		if (count >= ULONG_MAX / 2) {
-			msg_pinfo("timer loop overflow, reduced precision. ");
+			msg_pwarn("timer loop overflow (count=%lu, micro=%lu, timeusec=%lu), reduced precision.\n",
+				  count, micro, timeusec);
 			break;
 		}
 		count *= 2;
 	}
 	tries ++;
 
-	/* Avoid division by zero, but in that case the loop is shot anyway. */
-	if (!timeusec)
-		timeusec = 1;
+	/* Something is completely wrong. Avoid a possible endless loop and fall back to internal_delay(). */
+	if (timeusec == 0) {
+		micro = 0;
+		msg_pwarn("Timing measurement seems to be broken.\n"
+			  "Falling back to possibly insufficient timing sources!\n");
+		return 1;
+	}
 	
 	/* Compute rounded up number of loops per microsecond. */
 	micro = (count * micro) / timeusec + 1;
@@ -168,6 +173,7 @@  recalibrate:
 	msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
 
 	msg_pinfo("OK.\n");
+	return 0;
 }
 
 /* Not very precise sleep. */
@@ -187,7 +193,7 @@  void internal_sleep(unsigned int usecs)
 void internal_delay(unsigned int usecs)
 {
 	/* If the delay is >1 s, use internal_sleep because timing does not need to be so precise. */
-	if (usecs > 1000000) {
+	if (micro == 0 || usecs > 1000000) {
 		internal_sleep(usecs);
 	} else {
 		myusec_delay(usecs);
@@ -197,9 +203,9 @@  void internal_delay(unsigned int usecs)
 #else 
 #include <libpayload.h>
 
-void myusec_calibrate_delay(void)
+int myusec_calibrate_delay(void)
 {
-	get_cpu_speed();
+	return get_cpu_speed() == 0; // Return 1 on errors, i.e. if get_cpu_speed() returns 0
 }
 
 void internal_delay(unsigned int usecs)