Add antirollback clock support
+ /proc/ar_clock stores the antirollback clock, the earliest time_t which
the kernel will allow itself to be set to.
+ /proc/ar_clock needs to be set only once, at boot. The kernel will
continually advance it using the jiffies count.
Change-Id: Ifbdb5db7a8e183ffb7b52eb4034411f27f339cf4
diff --git a/arch/mips/configs/bruno_gfhd100b2_defconfig b/arch/mips/configs/bruno_gfhd100b2_defconfig
index 28fce5b..69f3bc6 100644
--- a/arch/mips/configs/bruno_gfhd100b2_defconfig
+++ b/arch/mips/configs/bruno_gfhd100b2_defconfig
@@ -259,6 +259,7 @@
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_AR_CLOCK=y
# CONFIG_HZ_48 is not set
# CONFIG_HZ_100 is not set
# CONFIG_HZ_128 is not set
diff --git a/include/linux/time.h b/include/linux/time.h
index 9f15ac7..17d90b7 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -120,6 +120,7 @@
extern int update_persistent_clock(struct timespec now);
extern int no_sync_cmos_clock __read_mostly;
void timekeeping_init(void);
+extern void timekeeping_late_init(void);
extern int timekeeping_suspended;
unsigned long get_seconds(void);
diff --git a/init/main.c b/init/main.c
index 8646401..441ae28 100644
--- a/init/main.c
+++ b/init/main.c
@@ -698,6 +698,8 @@
ftrace_init();
+ timekeeping_late_init();
+
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig
index f06a8a3..b598241 100644
--- a/kernel/time/Kconfig
+++ b/kernel/time/Kconfig
@@ -27,3 +27,12 @@
default y
depends on GENERIC_CLOCKEVENTS || GENERIC_CLOCKEVENTS_MIGR
+config AR_CLOCK
+ bool "Anti-Rollback Clock Support"
+ default n
+ help
+ This option enables anti-rollback protection of the system
+ clock. /proc/ar_clock stores the earliest time_t which the
+ kernel will allow itself to be set to. /proc/ar_clock needs
+ to be set only once, at boot. The kernel will continually
+ advance it using the jiffies count.
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 49010d8..32d95c24 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -21,6 +21,11 @@
#include <linux/tick.h>
#include <linux/stop_machine.h>
+#ifdef CONFIG_AR_CLOCK
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
+
/* Structure holding internal timekeeping values. */
struct timekeeper {
/* Current clocksource used for timekeeping. */
@@ -49,6 +54,61 @@
struct timekeeper timekeeper;
+#ifdef CONFIG_AR_CLOCK
+long anti_rollback_time = 0;
+u64 anti_rollback_jiffies = 0;
+
+static long get_antirollback_clock(void)
+{
+ if (anti_rollback_time == 0) return 0;
+
+ return anti_rollback_time +
+ ((get_jiffies_64() - anti_rollback_jiffies) / HZ);
+}
+
+static int ar_clock_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%lu\n", get_antirollback_clock());
+ return 0;
+}
+
+static ssize_t ar_clock_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *offs)
+{
+ char kbuf[32];
+ long t;
+
+ if (*offs || count > sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%lu", &t) == 1) {
+ anti_rollback_time = t;
+ anti_rollback_jiffies = get_jiffies_64();
+ } else {
+ kbuf[sizeof(kbuf)-1] = '\0';
+ printk(KERN_WARNING "unable to parse ar_clock: %s", kbuf);
+ }
+
+ return count;
+}
+
+static int ar_clock_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, ar_clock_show, NULL);
+}
+
+static const struct file_operations ar_clock_fops = {
+ .open = ar_clock_open,
+ .read = seq_read,
+ .write = ar_clock_write,
+ .release = single_release,
+};
+#endif
+
+
/**
* timekeeper_setup_internals - Set up internals to use clocksource clock.
*
@@ -311,6 +371,17 @@
struct timespec ts_delta;
unsigned long flags;
+#ifdef CONFIG_AR_CLOCK
+ /* No more than MAX_DELTA seconds before /proc/ar_clock */
+ #define MAX_DELTA (30*60)
+ long ar_clock = get_antirollback_clock();
+ if (ar_clock != 0 && tv->tv_sec < (ar_clock - MAX_DELTA)) {
+ printk(KERN_ERR "rejecting settimeofday %lu < antirollback %lu",
+ tv->tv_sec, ar_clock);
+ return -E2BIG;
+ }
+#endif
+
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
@@ -509,6 +580,14 @@
write_sequnlock_irqrestore(&xtime_lock, flags);
}
+void __init timekeeping_late_init(void)
+{
+#ifdef CONFIG_AR_CLOCK
+ if (proc_create("ar_clock", 0644, NULL, &ar_clock_fops) == NULL)
+ printk(KERN_ERR "timekeeping_late_init proc_create failed");
+#endif
+}
+
/* time in seconds when suspend began */
static struct timespec timekeeping_suspend_time;