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;