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.

Ported from:
https://gfiber.googlesource.com/kernel/bruno/+/489304aea9fb0d8d4206c24484bef8f96e0d7e3f%5E%5E!/
https://gfiber.googlesource.com/kernel/bruno/+/6d94cf7eae39171244e1e7171f1df86f6c9c88ef%5E!/

Change-Id: Ibafc784eb4bf491f6a575620d011163b949a6f92
diff --git a/arch/arm/configs/bruno_gfhd254_defconfig b/arch/arm/configs/bruno_gfhd254_defconfig
index 76f7905..52f69e1 100644
--- a/arch/arm/configs/bruno_gfhd254_defconfig
+++ b/arch/arm/configs/bruno_gfhd254_defconfig
@@ -3,6 +3,7 @@
 CONFIG_SYSVIPC=y
 CONFIG_POSIX_MQUEUE=y
 CONFIG_FHANDLE=y
+CONFIG_AR_CLOCK=y
 CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_IRQ_TIME_ACCOUNTING=y
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 99176af..176eaeb 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -3,6 +3,7 @@
 
 /* Included from linux/ktime.h */
 
+extern void timekeeping_late_init(void);
 void timekeeping_init(void);
 extern int timekeeping_suspended;
 
diff --git a/init/main.c b/init/main.c
index 2a89545..201d12e 100644
--- a/init/main.c
+++ b/init/main.c
@@ -674,6 +674,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 579ce1b..5e88d98 100644
--- a/kernel/time/Kconfig
+++ b/kernel/time/Kconfig
@@ -33,6 +33,16 @@
 config GENERIC_CLOCKEVENTS
 	bool
 
+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.
+
 # Architecture can handle broadcast in a driver-agnostic way
 config ARCH_HAS_TICK_BROADCAST
 	bool
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 65dbf8a..f9b565f 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -23,11 +23,17 @@
 #include <linux/stop_machine.h>
 #include <linux/pvclock_gtod.h>
 #include <linux/compiler.h>
+#include <asm/uaccess.h>
 
 #include "tick-internal.h"
 #include "ntp_internal.h"
 #include "timekeeping_internal.h"
 
+#ifdef CONFIG_AR_CLOCK
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
+
 #define TK_CLEAR_NTP		(1 << 0)
 #define TK_MIRROR		(1 << 1)
 #define TK_CLOCK_WAS_SET	(1 << 2)
@@ -227,6 +233,62 @@
 }
 #endif
 
+#ifdef CONFIG_AR_CLOCK
+long anti_rollback_time = 0;
+u64 anti_rollback_jiffies = 0;
+
+static long get_antirollback_clock(void)
+{
+	u64 tmp;
+	if (anti_rollback_time == 0) return 0;
+
+	tmp = get_jiffies_64() - anti_rollback_jiffies;
+	do_div(tmp, HZ);
+	return anti_rollback_time + tmp;
+}
+
+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;
+
+	kbuf[sizeof(kbuf)-1] = '\0';
+	if (sscanf(kbuf, "%lu", &t) == 1) {
+		anti_rollback_time = t;
+		anti_rollback_jiffies = get_jiffies_64();
+	} else {
+		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
+
 /**
  * tk_setup_internals - Set up internals to use clocksource clock.
  *
@@ -911,6 +973,17 @@
 	struct timespec64 ts_delta, xt;
 	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 && ts->tv_sec < (ar_clock - MAX_DELTA)) {
+		printk(KERN_ERR "rejecting settimeofday %lld < "
+			"antirollback %ld\n", ts->tv_sec, ar_clock);
+		return -EPERM;
+	}
+#endif
+
 	if (!timespec64_valid_strict(ts))
 		return -EINVAL;
 
@@ -1257,6 +1330,14 @@
 	raw_spin_unlock_irqrestore(&timekeeper_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\n");
+#endif
+}
+
 /* time in seconds when suspend began for persistent clock */
 static struct timespec64 timekeeping_suspend_time;