forked from Minki/linux
e1b6b6ce55
The vdso implementation of clock_getres currently returns 0 (success) whenever a null timespec is provided by the caller, regardless of the clock id supplied. This behavior is incorrect. It should fall back to syscall when an unrecognized clock id is passed, even when the timespec argument is null. This ensures that clock_getres always returns an error for invalid clock ids. Signed-off-by: Nathan Lynch <nathan_lynch@mentor.com> Acked-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
249 lines
5.1 KiB
ArmAsm
249 lines
5.1 KiB
ArmAsm
/*
|
|
* Userspace implementations of gettimeofday() and friends.
|
|
*
|
|
* Copyright (C) 2012 ARM Limited
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Will Deacon <will.deacon@arm.com>
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/asm-offsets.h>
|
|
#include <asm/unistd.h>
|
|
|
|
#define NSEC_PER_SEC_LO16 0xca00
|
|
#define NSEC_PER_SEC_HI16 0x3b9a
|
|
|
|
vdso_data .req x6
|
|
use_syscall .req w7
|
|
seqcnt .req w8
|
|
|
|
.macro seqcnt_acquire
|
|
9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
|
|
tbnz seqcnt, #0, 9999b
|
|
dmb ishld
|
|
ldr use_syscall, [vdso_data, #VDSO_USE_SYSCALL]
|
|
.endm
|
|
|
|
.macro seqcnt_read, cnt
|
|
dmb ishld
|
|
ldr \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
|
|
.endm
|
|
|
|
.macro seqcnt_check, cnt, fail
|
|
cmp \cnt, seqcnt
|
|
b.ne \fail
|
|
.endm
|
|
|
|
.text
|
|
|
|
/* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
|
|
ENTRY(__kernel_gettimeofday)
|
|
.cfi_startproc
|
|
mov x2, x30
|
|
.cfi_register x30, x2
|
|
|
|
/* Acquire the sequence counter and get the timespec. */
|
|
adr vdso_data, _vdso_data
|
|
1: seqcnt_acquire
|
|
cbnz use_syscall, 4f
|
|
|
|
/* If tv is NULL, skip to the timezone code. */
|
|
cbz x0, 2f
|
|
bl __do_get_tspec
|
|
seqcnt_check w9, 1b
|
|
|
|
/* Convert ns to us. */
|
|
mov x13, #1000
|
|
lsl x13, x13, x12
|
|
udiv x11, x11, x13
|
|
stp x10, x11, [x0, #TVAL_TV_SEC]
|
|
2:
|
|
/* If tz is NULL, return 0. */
|
|
cbz x1, 3f
|
|
ldp w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
|
|
stp w4, w5, [x1, #TZ_MINWEST]
|
|
3:
|
|
mov x0, xzr
|
|
ret x2
|
|
4:
|
|
/* Syscall fallback. */
|
|
mov x8, #__NR_gettimeofday
|
|
svc #0
|
|
ret x2
|
|
.cfi_endproc
|
|
ENDPROC(__kernel_gettimeofday)
|
|
|
|
/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
|
|
ENTRY(__kernel_clock_gettime)
|
|
.cfi_startproc
|
|
cmp w0, #CLOCK_REALTIME
|
|
ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
|
|
b.ne 2f
|
|
|
|
mov x2, x30
|
|
.cfi_register x30, x2
|
|
|
|
/* Get kernel timespec. */
|
|
adr vdso_data, _vdso_data
|
|
1: seqcnt_acquire
|
|
cbnz use_syscall, 7f
|
|
|
|
bl __do_get_tspec
|
|
seqcnt_check w9, 1b
|
|
|
|
mov x30, x2
|
|
|
|
cmp w0, #CLOCK_MONOTONIC
|
|
b.ne 6f
|
|
|
|
/* Get wtm timespec. */
|
|
ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
|
|
|
|
/* Check the sequence counter. */
|
|
seqcnt_read w9
|
|
seqcnt_check w9, 1b
|
|
b 4f
|
|
2:
|
|
cmp w0, #CLOCK_REALTIME_COARSE
|
|
ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
|
|
b.ne 8f
|
|
|
|
/* xtime_coarse_nsec is already right-shifted */
|
|
mov x12, #0
|
|
|
|
/* Get coarse timespec. */
|
|
adr vdso_data, _vdso_data
|
|
3: seqcnt_acquire
|
|
ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
|
|
|
|
/* Get wtm timespec. */
|
|
ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
|
|
|
|
/* Check the sequence counter. */
|
|
seqcnt_read w9
|
|
seqcnt_check w9, 3b
|
|
|
|
cmp w0, #CLOCK_MONOTONIC_COARSE
|
|
b.ne 6f
|
|
4:
|
|
/* Add on wtm timespec. */
|
|
add x10, x10, x13
|
|
lsl x14, x14, x12
|
|
add x11, x11, x14
|
|
|
|
/* Normalise the new timespec. */
|
|
mov x15, #NSEC_PER_SEC_LO16
|
|
movk x15, #NSEC_PER_SEC_HI16, lsl #16
|
|
lsl x15, x15, x12
|
|
cmp x11, x15
|
|
b.lt 5f
|
|
sub x11, x11, x15
|
|
add x10, x10, #1
|
|
5:
|
|
cmp x11, #0
|
|
b.ge 6f
|
|
add x11, x11, x15
|
|
sub x10, x10, #1
|
|
|
|
6: /* Store to the user timespec. */
|
|
lsr x11, x11, x12
|
|
stp x10, x11, [x1, #TSPEC_TV_SEC]
|
|
mov x0, xzr
|
|
ret
|
|
7:
|
|
mov x30, x2
|
|
8: /* Syscall fallback. */
|
|
mov x8, #__NR_clock_gettime
|
|
svc #0
|
|
ret
|
|
.cfi_endproc
|
|
ENDPROC(__kernel_clock_gettime)
|
|
|
|
/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
|
|
ENTRY(__kernel_clock_getres)
|
|
.cfi_startproc
|
|
cmp w0, #CLOCK_REALTIME
|
|
ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
|
|
b.ne 1f
|
|
|
|
ldr x2, 5f
|
|
b 2f
|
|
1:
|
|
cmp w0, #CLOCK_REALTIME_COARSE
|
|
ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
|
|
b.ne 4f
|
|
ldr x2, 6f
|
|
2:
|
|
cbz w1, 3f
|
|
stp xzr, x2, [x1]
|
|
|
|
3: /* res == NULL. */
|
|
mov w0, wzr
|
|
ret
|
|
|
|
4: /* Syscall fallback. */
|
|
mov x8, #__NR_clock_getres
|
|
svc #0
|
|
ret
|
|
5:
|
|
.quad CLOCK_REALTIME_RES
|
|
6:
|
|
.quad CLOCK_COARSE_RES
|
|
.cfi_endproc
|
|
ENDPROC(__kernel_clock_getres)
|
|
|
|
/*
|
|
* Read the current time from the architected counter.
|
|
* Expects vdso_data to be initialised.
|
|
* Clobbers the temporary registers (x9 - x15).
|
|
* Returns:
|
|
* - w9 = vDSO sequence counter
|
|
* - (x10, x11) = (ts->tv_sec, shifted ts->tv_nsec)
|
|
* - w12 = cs_shift
|
|
*/
|
|
ENTRY(__do_get_tspec)
|
|
.cfi_startproc
|
|
|
|
/* Read from the vDSO data page. */
|
|
ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
|
|
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
|
|
ldp w11, w12, [vdso_data, #VDSO_CS_MULT]
|
|
seqcnt_read w9
|
|
|
|
/* Read the virtual counter. */
|
|
isb
|
|
mrs x15, cntvct_el0
|
|
|
|
/* Calculate cycle delta and convert to ns. */
|
|
sub x10, x15, x10
|
|
/* We can only guarantee 56 bits of precision. */
|
|
movn x15, #0xff00, lsl #48
|
|
and x10, x15, x10
|
|
mul x10, x10, x11
|
|
|
|
/* Use the kernel time to calculate the new timespec. */
|
|
mov x11, #NSEC_PER_SEC_LO16
|
|
movk x11, #NSEC_PER_SEC_HI16, lsl #16
|
|
lsl x11, x11, x12
|
|
add x15, x10, x14
|
|
udiv x14, x15, x11
|
|
add x10, x13, x14
|
|
mul x13, x14, x11
|
|
sub x11, x15, x13
|
|
|
|
ret
|
|
.cfi_endproc
|
|
ENDPROC(__do_get_tspec)
|