linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jiri Bohac <jbohac@suse.cz>
To: Thomas Gleixner <tglx@linutronix.de>,
	Ingo Molnar <mingo@redhat.com>, "H. Peter Anvin" <hpa@zytor.com>
Cc: Borislav Petkov <bp@suse.de>, linux-kernel@vger.kernel.org
Subject: [PATCH] x86_64: use HPET in 64-bit mode to avoid a harware 32-bit wraparound bug
Date: Fri, 8 Dec 2017 18:17:00 +0100	[thread overview]
Message-ID: <20171208171700.pbridtbt3tkvjbhi@dwarf.suse.cz> (raw)

The HPET clockevent configures the HPET Timer 0 in 32-bit mode. With a
wrap-around time of >4 minutes, this is OK for the clockevent purposes.
Unfortunately, some chipsets contain a documented bug, where this setting
also makes the HPET Main Counter wrap to 32 bits. HPET can be mmapped to
userspace, which makes this bug visible to applications.

The original HPET specification 1.0 from 2004 does not mention any
side-effects of setting TN_32MODE_CNF on the individual timers. But e.g.
the ICH9 documentation documents this bug:

    NOTE: When this bit is set to ‘1’, the hardware counter will
    do a 32-bit operation on comparator match and rollovers, thus
    the upper 32-bit of the Timer 0 Comparator Value register is
    ignored. The upper 32-bit of the main counter is not involved
    in any rollover from lower 32-bit of the main counter and
    becomes all zeros.

(see http://www.intel.com/assets/pdf/datasheet/316972.pdf, page
819, section 21.1.5, Bit 8). 
I've seen this behaviour on many other Intel chipsets as well.

This patch changes the HPET clockevent code to use the Timer 0 in 64-bit
mode (if compiled for x86_64; doing this for 32-bit kernels would bring
a performance penalty as a single read would require three 32-bit HPET
accesses).

Signed-off-by: Jiri Bohac <jbohac@suse.cz>

diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 8ce4212e2b8d..1a500220d0dc 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -69,6 +69,31 @@ static inline void hpet_writel(unsigned int d, unsigned int a)
 	writel(d, hpet_virt_address + a);
 }
 
+
+static inline unsigned long hpet_read_long(unsigned int a)
+{
+#ifdef CONFIG_64BIT
+	return readq(hpet_virt_address + a);
+#else
+	return readl(hpet_virt_address + a);
+#endif
+}
+
+static inline void hpet_write_long(unsigned long d, unsigned int a)
+{
+#ifdef CONFIG_64BIT
+	writeq(d, hpet_virt_address + a);
+#else
+	writel(d, hpet_virt_address + a);
+#endif
+}
+
+#ifdef CONFIG_64BIT
+#define HPET_TN_WIDTH_FLAG 0
+#else
+#define HPET_TN_WIDTH_FLAG HPET_TN_32BIT
+#endif
+
 #ifdef CONFIG_X86_64
 #include <asm/pgtable.h>
 #endif
@@ -294,19 +319,20 @@ static void hpet_legacy_clockevent_register(void)
 
 static int hpet_set_periodic(struct clock_event_device *evt, int timer)
 {
-	unsigned int cfg, cmp, now;
+	unsigned int cfg;
+	unsigned long cmp, now;
 	uint64_t delta;
 
 	hpet_stop_counter();
 	delta = ((uint64_t)(NSEC_PER_SEC / HZ)) * evt->mult;
 	delta >>= evt->shift;
-	now = hpet_readl(HPET_COUNTER);
-	cmp = now + (unsigned int)delta;
+	now = hpet_read_long(HPET_COUNTER);
+	cmp = now + (unsigned long)delta;
 	cfg = hpet_readl(HPET_Tn_CFG(timer));
 	cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
-	       HPET_TN_32BIT;
+	       HPET_TN_WIDTH_FLAG;
 	hpet_writel(cfg, HPET_Tn_CFG(timer));
-	hpet_writel(cmp, HPET_Tn_CMP(timer));
+	hpet_write_long(cmp, HPET_Tn_CMP(timer));
 	udelay(1);
 	/*
 	 * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL
@@ -315,7 +341,7 @@ static int hpet_set_periodic(struct clock_event_device *evt, int timer)
 	 * (See AMD-8111 HyperTransport I/O Hub Data Sheet,
 	 * Publication # 24674)
 	 */
-	hpet_writel((unsigned int)delta, HPET_Tn_CMP(timer));
+	hpet_write_long((unsigned long)delta, HPET_Tn_CMP(timer));
 	hpet_start_counter();
 	hpet_print_config();
 
@@ -328,7 +354,7 @@ static int hpet_set_oneshot(struct clock_event_device *evt, int timer)
 
 	cfg = hpet_readl(HPET_Tn_CFG(timer));
 	cfg &= ~HPET_TN_PERIODIC;
-	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
+	cfg |= HPET_TN_ENABLE | HPET_TN_WIDTH_FLAG;
 	hpet_writel(cfg, HPET_Tn_CFG(timer));
 
 	return 0;
@@ -355,12 +381,12 @@ static int hpet_resume(struct clock_event_device *evt)
 static int hpet_next_event(unsigned long delta,
 			   struct clock_event_device *evt, int timer)
 {
-	u32 cnt;
-	s32 res;
+	unsigned long cnt;
+	long res;
 
-	cnt = hpet_readl(HPET_COUNTER);
-	cnt += (u32) delta;
-	hpet_writel(cnt, HPET_Tn_CMP(timer));
+	cnt = hpet_read_long(HPET_COUNTER);
+	cnt += delta;
+	hpet_write_long(cnt, HPET_Tn_CMP(timer));
 
 	/*
 	 * HPETs are a complete disaster. The compare register is
@@ -384,7 +410,7 @@ static int hpet_next_event(unsigned long delta,
 	 * the event. The minimum programming delta for the generic
 	 * clockevents code is set to 1.5 * HPET_MIN_CYCLES.
 	 */
-	res = (s32)(cnt - hpet_readl(HPET_COUNTER));
+	res = (long)(cnt - hpet_read_long(HPET_COUNTER));
 
 	return res < HPET_MIN_CYCLES ? -ETIME : 0;
 }
-- 
Jiri Bohac <jbohac@suse.cz>
SUSE Labs, Prague, Czechia

             reply	other threads:[~2017-12-08 17:17 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-08 17:17 Jiri Bohac [this message]
2017-12-28 11:49 ` [PATCH] x86_64: use HPET in 64-bit mode to avoid a harware 32-bit wraparound bug Thomas Gleixner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171208171700.pbridtbt3tkvjbhi@dwarf.suse.cz \
    --to=jbohac@suse.cz \
    --cc=bp@suse.de \
    --cc=hpa@zytor.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).