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

Commit 1a596398 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

sparc64: add custom adjtimex/clock_adjtime functions



sparc64 is the only architecture on Linux that has a 'timeval'
definition with a 32-bit tv_usec but a 64-bit tv_sec. This causes
problems for sparc32 compat mode when we convert it to use the
new __kernel_timex type that has the same layout as all other
64-bit architectures.

To avoid adding sparc64 specific code into the generic adjtimex
implementation, this adds a wrapper in the sparc64 system call handling
that converts the sparc64 'timex' into the new '__kernel_timex'.

At this point, the two structures are defined to be identical,
but that will change in the next step once we convert sparc32.

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 50b93f30
Loading
Loading
Loading
Loading
+58 −1
Original line number Diff line number Diff line
@@ -28,8 +28,9 @@
#include <linux/random.h>
#include <linux/export.h>
#include <linux/context_tracking.h>

#include <linux/timex.h>
#include <linux/uaccess.h>

#include <asm/utrap.h>
#include <asm/unistd.h>

@@ -544,6 +545,62 @@ SYSCALL_DEFINE2(getdomainname, char __user *, name, int, len)
	return err;
}

SYSCALL_DEFINE1(sparc_adjtimex, struct timex __user *, txc_p)
{
	struct timex txc;		/* Local copy of parameter */
	struct timex *kt = (void *)&txc;
	int ret;

	/* Copy the user data space into the kernel copy
	 * structure. But bear in mind that the structures
	 * may change
	 */
	if (copy_from_user(&txc, txc_p, sizeof(struct timex)))
		return -EFAULT;

	/*
	 * override for sparc64 specific timeval type: tv_usec
	 * is 32 bit wide instead of 64-bit in __kernel_timex
	 */
	kt->time.tv_usec = txc.time.tv_usec;
	ret = do_adjtimex(kt);
	txc.time.tv_usec = kt->time.tv_usec;

	return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret;
}

SYSCALL_DEFINE2(sparc_clock_adjtime, const clockid_t, which_clock,struct timex __user *, txc_p)
{
	struct timex txc;		/* Local copy of parameter */
	struct timex *kt = (void *)&txc;
	int ret;

	if (!IS_ENABLED(CONFIG_POSIX_TIMERS)) {
		pr_err_once("process %d (%s) attempted a POSIX timer syscall "
		    "while CONFIG_POSIX_TIMERS is not set\n",
		    current->pid, current->comm);

		return -ENOSYS;
	}

	/* Copy the user data space into the kernel copy
	 * structure. But bear in mind that the structures
	 * may change
	 */
	if (copy_from_user(&txc, txc_p, sizeof(struct timex)))
		return -EFAULT;

	/*
	 * override for sparc64 specific timeval type: tv_usec
	 * is 32 bit wide instead of 64-bit in __kernel_timex
	 */
	kt->time.tv_usec = txc.time.tv_usec;
	ret = do_clock_adjtime(which_clock, kt);
	txc.time.tv_usec = kt->time.tv_usec;

	return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret;
}

SYSCALL_DEFINE5(utrap_install, utrap_entry_t, type,
		utrap_handler_t, new_p, utrap_handler_t, new_d,
		utrap_handler_t __user *, old_p,
+4 −2
Original line number Diff line number Diff line
@@ -258,7 +258,8 @@
216	64	sigreturn		sys_nis_syscall
217	common	clone			sys_clone
218	common	ioprio_get		sys_ioprio_get
219	common	adjtimex		sys_adjtimex			compat_sys_adjtimex
219	32	adjtimex		sys_adjtimex			compat_sys_adjtimex
219	64	adjtimex		sys_sparc_adjtimex
220	32	sigprocmask		sys_sigprocmask			compat_sys_sigprocmask
220	64	sigprocmask		sys_nis_syscall
221	common	create_module		sys_ni_syscall
@@ -377,7 +378,8 @@
331	common	prlimit64		sys_prlimit64
332	common	name_to_handle_at	sys_name_to_handle_at
333	common	open_by_handle_at	sys_open_by_handle_at		compat_sys_open_by_handle_at
334	common	clock_adjtime		sys_clock_adjtime		compat_sys_clock_adjtime
334	32	clock_adjtime		sys_clock_adjtime		compat_sys_clock_adjtime
334	64	clock_adjtime		sys_sparc_clock_adjtime
335	common	syncfs			sys_syncfs
336	common	sendmmsg		sys_sendmmsg			compat_sys_sendmmsg
337	common	setns			sys_setns
+2 −0
Original line number Diff line number Diff line
@@ -159,6 +159,8 @@ extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */
#define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)

extern int do_adjtimex(struct timex *);
extern int do_clock_adjtime(const clockid_t which_clock, struct timex * ktx);

extern void hardpps(const struct timespec64 *, const struct timespec64 *);

int read_current_timer(unsigned long *timer_val);
+12 −12
Original line number Diff line number Diff line
@@ -1047,22 +1047,28 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
	return error;
}

SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
		struct timex __user *, utx)
int do_clock_adjtime(const clockid_t which_clock, struct timex * ktx)
{
	const struct k_clock *kc = clockid_to_kclock(which_clock);
	struct timex ktx;
	int err;

	if (!kc)
		return -EINVAL;
	if (!kc->clock_adj)
		return -EOPNOTSUPP;

	return kc->clock_adj(which_clock, ktx);
}

SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
		struct timex __user *, utx)
{
	struct timex ktx;
	int err;

	if (copy_from_user(&ktx, utx, sizeof(ktx)))
		return -EFAULT;

	err = kc->clock_adj(which_clock, &ktx);
	err = do_clock_adjtime(which_clock, &ktx);

	if (err >= 0 && copy_to_user(utx, &ktx, sizeof(ktx)))
		return -EFAULT;
@@ -1126,20 +1132,14 @@ COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock,
COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock,
		       struct old_timex32 __user *, utp)
{
	const struct k_clock *kc = clockid_to_kclock(which_clock);
	struct timex ktx;
	int err;

	if (!kc)
		return -EINVAL;
	if (!kc->clock_adj)
		return -EOPNOTSUPP;

	err = get_old_timex32(&ktx, utp);
	if (err)
		return err;

	err = kc->clock_adj(which_clock, &ktx);
	err = do_clock_adjtime(which_clock, &ktx);

	if (err >= 0)
		err = put_old_timex32(utp, &ktx);