Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e2ef5e4 authored by Martin Schwidefsky's avatar Martin Schwidefsky
Browse files

s390/time: add support for the TOD clock epoch extension



The TOD epoch extension adds 8 epoch bits to the TOD clock to provide
a continuous clock after 2042/09/17. The store-clock-extended (STCKE)
instruction will store the epoch index in the first byte of the
16 bytes stored by the instruction. The read_boot_clock64 and the
read_presistent_clock64 functions need to take the additional bits
into account to give the correct result after 2042/09/17.

The clock-comparator register will stay 64 bit wide. The comparison
of the clock-comparator with the TOD clock is limited to bytes
1 to 8 of the extended TOD format. To deal with the overflow problem
due to an epoch change the clock-comparator sign control in CR0 can
be used to switch the comparison of the 64-bit TOD clock with the
clock-comparator to a signed comparison.

The decision between the signed vs. unsigned clock-comparator
comparisons is done at boot time. Only if the TOD clock is in the
second half of a 142 year epoch the signed comparison is used.
This solves the epoch overflow issue as long as the machine is
booted at least once in an epoch.

Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 45be0a02
Loading
Loading
Loading
Loading
+24 −24
Original line number Diff line number Diff line
@@ -95,46 +95,46 @@ struct lowcore {
	__u64	int_clock;			/* 0x0310 */
	__u64	mcck_clock;			/* 0x0318 */
	__u64	clock_comparator;		/* 0x0320 */
	__u64	boot_clock[2];			/* 0x0328 */

	/* Current process. */
	__u64	current_task;			/* 0x0328 */
	__u8	pad_0x318[0x320-0x318];		/* 0x0330 */
	__u64	kernel_stack;			/* 0x0338 */
	__u64	current_task;			/* 0x0338 */
	__u64	kernel_stack;			/* 0x0340 */

	/* Interrupt, panic and restart stack. */
	__u64	async_stack;			/* 0x0340 */
	__u64	panic_stack;			/* 0x0348 */
	__u64	restart_stack;			/* 0x0350 */
	__u64	async_stack;			/* 0x0348 */
	__u64	panic_stack;			/* 0x0350 */
	__u64	restart_stack;			/* 0x0358 */

	/* Restart function and parameter. */
	__u64	restart_fn;			/* 0x0358 */
	__u64	restart_data;			/* 0x0360 */
	__u64	restart_source;			/* 0x0368 */
	__u64	restart_fn;			/* 0x0360 */
	__u64	restart_data;			/* 0x0368 */
	__u64	restart_source;			/* 0x0370 */

	/* Address space pointer. */
	__u64	kernel_asce;			/* 0x0370 */
	__u64	user_asce;			/* 0x0378 */
	__u64	kernel_asce;			/* 0x0378 */
	__u64	user_asce;			/* 0x0380 */

	/*
	 * The lpp and current_pid fields form a
	 * 64-bit value that is set as program
	 * parameter with the LPP instruction.
	 */
	__u32	lpp;				/* 0x0380 */
	__u32	current_pid;			/* 0x0384 */
	__u32	lpp;				/* 0x0388 */
	__u32	current_pid;			/* 0x038c */

	/* SMP info area */
	__u32	cpu_nr;				/* 0x0388 */
	__u32	softirq_pending;		/* 0x038c */
	__u64	percpu_offset;			/* 0x0390 */
	__u64	vdso_per_cpu_data;		/* 0x0398 */
	__u64	machine_flags;			/* 0x03a0 */
	__u32	preempt_count;			/* 0x03a8 */
	__u8	pad_0x03ac[0x03b0-0x03ac];	/* 0x03ac */
	__u64	gmap;				/* 0x03b0 */
	__u32	spinlock_lockval;		/* 0x03b8 */
	__u32	fpu_flags;			/* 0x03bc */
	__u8	pad_0x03c0[0x0400-0x03c0];	/* 0x03c0 */
	__u32	cpu_nr;				/* 0x0390 */
	__u32	softirq_pending;		/* 0x0394 */
	__u64	percpu_offset;			/* 0x0398 */
	__u64	vdso_per_cpu_data;		/* 0x03a0 */
	__u64	machine_flags;			/* 0x03a8 */
	__u32	preempt_count;			/* 0x03b0 */
	__u8	pad_0x03b4[0x03b8-0x03b4];	/* 0x03b4 */
	__u64	gmap;				/* 0x03b8 */
	__u32	spinlock_lockval;		/* 0x03c0 */
	__u32	fpu_flags;			/* 0x03c4 */
	__u8	pad_0x03c8[0x0400-0x03c8];	/* 0x03c8 */

	/* Per cpu primary space access list */
	__u32	paste[16];			/* 0x0400 */
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#define MACHINE_FLAG_TLB_GUEST	_BITUL(14)
#define MACHINE_FLAG_NX		_BITUL(15)
#define MACHINE_FLAG_GS		_BITUL(16)
#define MACHINE_FLAG_SCC	_BITUL(17)

#define LPP_MAGIC		_BITUL(31)
#define LPP_PFAULT_PID_MASK	_AC(0xffffffff, UL)
@@ -72,6 +73,7 @@ extern void detect_memory_memblock(void);
#define MACHINE_HAS_TLB_GUEST	(S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST)
#define MACHINE_HAS_NX		(S390_lowcore.machine_flags & MACHINE_FLAG_NX)
#define MACHINE_HAS_GS		(S390_lowcore.machine_flags & MACHINE_FLAG_GS)
#define MACHINE_HAS_SCC		(S390_lowcore.machine_flags & MACHINE_FLAG_SCC)

/*
 * Console mode. Override with conmode=
+34 −4
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
/* The value of the TOD clock for 1.1.1970. */
#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL

extern u64 clock_comparator_max;

/* Inline functions for clock register access. */
static inline int set_tod_clock(__u64 time)
{
@@ -126,7 +128,7 @@ static inline unsigned long long local_tick_disable(void)
	unsigned long long old;

	old = S390_lowcore.clock_comparator;
	S390_lowcore.clock_comparator = -1ULL;
	S390_lowcore.clock_comparator = clock_comparator_max;
	set_clock_comparator(S390_lowcore.clock_comparator);
	return old;
}
@@ -178,20 +180,20 @@ int get_phys_clock(unsigned long long *clock);
void init_cpu_timer(void);
unsigned long long monotonic_clock(void);

extern u64 sched_clock_base_cc;
extern unsigned char tod_clock_base[16] __aligned(8);

/**
 * get_clock_monotonic - returns current time in clock rate units
 *
 * The caller must ensure that preemption is disabled.
 * The clock and sched_clock_base get changed via stop_machine.
 * The clock and tod_clock_base get changed via stop_machine.
 * Therefore preemption must be disabled when calling this
 * function, otherwise the returned value is not guaranteed to
 * be monotonic.
 */
static inline unsigned long long get_tod_clock_monotonic(void)
{
	return get_tod_clock() - sched_clock_base_cc;
	return get_tod_clock() - *(unsigned long long *) &tod_clock_base[1];
}

/**
@@ -218,4 +220,32 @@ static inline unsigned long long tod_to_ns(unsigned long long todval)
	return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9);
}

/**
 * tod_after - compare two 64 bit TOD values
 * @a: first 64 bit TOD timestamp
 * @b: second 64 bit TOD timestamp
 *
 * Returns: true if a is later than b
 */
static inline int tod_after(unsigned long long a, unsigned long long b)
{
	if (MACHINE_HAS_SCC)
		return (long long) a > (long long) b;
	return a > b;
}

/**
 * tod_after_eq - compare two 64 bit TOD values
 * @a: first 64 bit TOD timestamp
 * @b: second 64 bit TOD timestamp
 *
 * Returns: true if a is later than b
 */
static inline int tod_after_eq(unsigned long long a, unsigned long long b)
{
	if (MACHINE_HAS_SCC)
		return (long long) a >= (long long) b;
	return a >= b;
}

#endif
+1 −0
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@ int main(void)
	OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
	OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
	OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
	OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock);
	OFFSET(__LC_CURRENT, lowcore, current_task);
	OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
	OFFSET(__LC_ASYNC_STACK, lowcore, async_stack);
+5 −4
Original line number Diff line number Diff line
@@ -866,7 +866,8 @@ static inline void
debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level,
			int exception)
{
	active->id.stck = get_tod_clock_fast() - sched_clock_base_cc;
	active->id.stck = get_tod_clock_fast() -
		*(unsigned long long *) &tod_clock_base[1];
	active->id.fields.cpuid = smp_processor_id();
	active->caller = __builtin_return_address(0);
	active->id.fields.exception = exception;
@@ -1455,15 +1456,15 @@ int
debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
			 int area, debug_entry_t * entry, char *out_buf)
{
	unsigned long sec, usec;
	unsigned long base, sec, usec;
	char *except_str;
	unsigned long caller;
	int rc = 0;
	unsigned int level;

	level = entry->id.fields.level;
	sec = (entry->id.stck >> 12) + (sched_clock_base_cc >> 12);
	sec = sec - (TOD_UNIX_EPOCH >> 12);
	base = (*(unsigned long *) &tod_clock_base[0]) >> 4;
	sec = (entry->id.stck >> 12) + base - (TOD_UNIX_EPOCH >> 12);
	usec = do_div(sec, USEC_PER_SEC);

	if (entry->id.fields.exception)
Loading