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;