123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /*
- * 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)
|