2012-03-05 11:49:31 +00:00
/ *
* Userspace i m p l e m e n t a t i o n s o f g e t t i m e o f d a y ( ) a n d f r i e n d s .
*
* Copyright ( C ) 2 0 1 2 A R M L i m i t e d
*
* This p r o g r a m i s f r e e s o f t w a r e ; you can redistribute it and/or modify
* it u n d e r t h e t e r m s o f t h e G N U G e n e r a l P u b l i c L i c e n s e v e r s i o n 2 a s
* published b y t h e F r e e S o f t w a r e F o u n d a t i o n .
*
* This p r o g r a m i s d i s t r i b u t e d i n t h e h o p e t h a t i t w i l l b e u s e f u l ,
* but W I T H O U T A N Y W A R R A N T Y ; without even the implied warranty of
* MERCHANTABILITY o r F I T N E S S F O R A P A R T I C U L A R P U R P O S E . S e e t h e
* GNU G e n e r a l P u b l i c L i c e n s e f o r m o r e d e t a i l s .
*
* You s h o u l d h a v e r e c e i v e d a c o p y o f t h e G N U G e n e r a l P u b l i c L i c e n s e
* along w i t h t h i s p r o g r a m . I f n o t , s e e < h t t p : / / w w w . g n u . o r g / l i c e n s e s / > .
*
* Author : Will D e a c o n < w i l l . d e a c o n @arm.com>
* /
# include < l i n u x / l i n k a g e . h >
# include < a s m / a s m - o f f s e t s . h >
# include < a s m / u n i s t d . h >
# define N S E C _ P E R _ S E C _ L O 1 6 0 x c a00
# define N S E C _ P E R _ S E C _ H I 1 6 0 x3 b9 a
vdso_ d a t a . r e q x6
use_ s y s c a l l . r e q w7
seqcnt . r e q w8
.macro seqcnt_acquire
9999 : ldr s e q c n t , [ v d s o _ d a t a , #V D S O _ T B _ S E Q _ C O U N T ]
tbnz s e q c n t , #0 , 9 9 9 9 b
dmb i s h l d
ldr u s e _ s y s c a l l , [ v d s o _ d a t a , #V D S O _ U S E _ S Y S C A L L ]
.endm
.macro seqcnt_ r e a d , c n t
dmb i s h l d
ldr \ c n t , [ v d s o _ d a t a , #V D S O _ T B _ S E Q _ C O U N T ]
.endm
.macro seqcnt_ c h e c k , c n t , f a i l
cmp \ c n t , s e q c n t
b. n e \ f a i l
.endm
.text
/* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
ENTRY( _ _ k e r n e l _ g e t t i m e o f d a y )
.cfi_startproc
mov x2 , x30
.cfi_register x3 0 , x2
/* Acquire the sequence counter and get the timespec. */
adr v d s o _ d a t a , _ v d s o _ d a t a
1 : seqcnt_ a c q u i r e
cbnz u s e _ s y s c a l l , 4 f
/* If tv is NULL, skip to the timezone code. */
cbz x0 , 2 f
bl _ _ d o _ g e t _ t s p e c
2012-11-29 22:19:01 +00:00
seqcnt_ c h e c k w9 , 1 b
2012-03-05 11:49:31 +00:00
/* Convert ns to us. */
2012-11-29 22:19:01 +00:00
mov x13 , #1000
2012-11-29 22:33:29 +00:00
lsl x13 , x13 , x12
2012-11-29 22:19:01 +00:00
udiv x11 , x11 , x13
stp x10 , x11 , [ x0 , #T V A L _ T V _ S E C ]
2012-03-05 11:49:31 +00:00
2 :
/* If tz is NULL, return 0. */
cbz x1 , 3 f
ldp w4 , w5 , [ v d s o _ d a t a , #V D S O _ T Z _ M I N W E S T ]
stp w4 , w5 , [ x1 , #T Z _ M I N W E S T ]
3 :
mov x0 , x z r
ret x2
4 :
/* Syscall fallback. */
mov x8 , #_ _ N R _ g e t t i m e o f d a y
svc #0
ret x2
.cfi_endproc
ENDPROC( _ _ k e r n e l _ g e t t i m e o f d a y )
/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
ENTRY( _ _ k e r n e l _ c l o c k _ g e t t i m e )
.cfi_startproc
cmp w0 , #C L O C K _ R E A L T I M E
ccmp w0 , #C L O C K _ M O N O T O N I C , # 0x4 , n e
b. n e 2 f
mov x2 , x30
.cfi_register x3 0 , x2
/* Get kernel timespec. */
adr v d s o _ d a t a , _ v d s o _ d a t a
1 : seqcnt_ a c q u i r e
cbnz u s e _ s y s c a l l , 7 f
bl _ _ d o _ g e t _ t s p e c
2012-11-29 22:19:01 +00:00
seqcnt_ c h e c k w9 , 1 b
2012-03-05 11:49:31 +00:00
cmp w0 , #C L O C K _ M O N O T O N I C
b. n e 6 f
/* Get wtm timespec. */
2012-11-29 22:19:01 +00:00
ldp x13 , x14 , [ v d s o _ d a t a , #V D S O _ W T M _ C L K _ S E C ]
2012-03-05 11:49:31 +00:00
/* Check the sequence counter. */
2012-11-29 22:19:01 +00:00
seqcnt_ r e a d w9
seqcnt_ c h e c k w9 , 1 b
2012-03-05 11:49:31 +00:00
b 4 f
2 :
cmp w0 , #C L O C K _ R E A L T I M E _ C O A R S E
ccmp w0 , #C L O C K _ M O N O T O N I C _ C O A R S E , # 0x4 , n e
b. n e 8 f
/* Get coarse timespec. */
adr v d s o _ d a t a , _ v d s o _ d a t a
3 : seqcnt_ a c q u i r e
2012-11-29 22:19:01 +00:00
ldp x10 , x11 , [ v d s o _ d a t a , #V D S O _ X T I M E _ C R S _ S E C ]
2012-03-05 11:49:31 +00:00
/* Get wtm timespec. */
2012-11-29 22:19:01 +00:00
ldp x13 , x14 , [ v d s o _ d a t a , #V D S O _ W T M _ C L K _ S E C ]
2012-03-05 11:49:31 +00:00
/* Check the sequence counter. */
2012-11-29 22:19:01 +00:00
seqcnt_ r e a d w9
seqcnt_ c h e c k w9 , 3 b
2012-11-29 22:11:51 +00:00
cmp w0 , #C L O C K _ M O N O T O N I C _ C O A R S E
b. n e 6 f
2012-03-05 11:49:31 +00:00
4 :
/* Add on wtm timespec. */
2012-11-29 22:19:01 +00:00
add x10 , x10 , x13
2012-11-29 22:33:29 +00:00
lsl x14 , x14 , x12
2012-11-29 22:19:01 +00:00
add x11 , x11 , x14
2012-03-05 11:49:31 +00:00
/* Normalise the new timespec. */
2012-11-29 22:19:01 +00:00
mov x15 , #N S E C _ P E R _ S E C _ L O 16
movk x15 , #N S E C _ P E R _ S E C _ H I 16 , l s l #16
2012-11-29 22:33:29 +00:00
lsl x15 , x15 , x12
2012-11-29 22:19:01 +00:00
cmp x11 , x15
2012-03-05 11:49:31 +00:00
b. l t 5 f
2012-11-29 22:19:01 +00:00
sub x11 , x11 , x15
add x10 , x10 , #1
2012-03-05 11:49:31 +00:00
5 :
2012-11-29 22:19:01 +00:00
cmp x11 , #0
2012-03-05 11:49:31 +00:00
b. g e 6 f
2012-11-29 22:19:01 +00:00
add x11 , x11 , x15
sub x10 , x10 , #1
2012-03-05 11:49:31 +00:00
6 : /* Store to the user timespec. */
2012-11-29 22:33:29 +00:00
lsr x11 , x11 , x12
2012-11-29 22:19:01 +00:00
stp x10 , x11 , [ x1 , #T S P E C _ T V _ S E C ]
2012-03-05 11:49:31 +00:00
mov x0 , x z r
ret x2
7 :
mov x30 , x2
8 : /* Syscall fallback. */
mov x8 , #_ _ N R _ c l o c k _ g e t t i m e
svc #0
ret
.cfi_endproc
ENDPROC( _ _ k e r n e l _ c l o c k _ g e t t i m e )
/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
ENTRY( _ _ k e r n e l _ c l o c k _ g e t r e s )
.cfi_startproc
cbz w1 , 3 f
cmp w0 , #C L O C K _ R E A L T I M E
ccmp w0 , #C L O C K _ M O N O T O N I C , # 0x4 , n e
b. n e 1 f
ldr x2 , 5 f
b 2 f
1 :
cmp w0 , #C L O C K _ R E A L T I M E _ C O A R S E
ccmp w0 , #C L O C K _ M O N O T O N I C _ C O A R S E , # 0x4 , n e
b. n e 4 f
ldr x2 , 6 f
2 :
stp x z r , x2 , [ x1 ]
3 : /* res == NULL. */
mov w0 , w z r
ret
4 : /* Syscall fallback. */
mov x8 , #_ _ N R _ c l o c k _ g e t r e s
svc #0
ret
5 :
.quad CLOCK_REALTIME_RES
6 :
.quad CLOCK_COARSE_RES
.cfi_endproc
ENDPROC( _ _ k e r n e l _ c l o c k _ g e t r e s )
/ *
* Read t h e c u r r e n t t i m e f r o m t h e a r c h i t e c t e d c o u n t e r .
* Expects v d s o _ d a t a t o b e i n i t i a l i s e d .
* Clobbers t h e t e m p o r a r y r e g i s t e r s ( x9 - x15 ) .
* Returns :
2012-11-29 22:19:01 +00:00
* - w9 = v D S O s e q u e n c e c o u n t e r
2012-11-29 22:33:29 +00:00
* - ( x1 0 , x11 ) = ( t s - > t v _ s e c , s h i f t e d t s - > t v _ n s e c )
2012-11-29 22:19:01 +00:00
* - w1 2 = c s _ s h i f t
2012-03-05 11:49:31 +00:00
* /
ENTRY( _ _ d o _ g e t _ t s p e c )
.cfi_startproc
/* Read from the vDSO data page. */
ldr x10 , [ v d s o _ d a t a , #V D S O _ C S _ C Y C L E _ L A S T ]
2012-11-29 22:19:01 +00:00
ldp x13 , x14 , [ v d s o _ d a t a , #V D S O _ X T I M E _ C L K _ S E C ]
ldp w11 , w12 , [ v d s o _ d a t a , #V D S O _ C S _ M U L T ]
seqcnt_ r e a d w9
2012-03-05 11:49:31 +00:00
2012-11-29 22:48:31 +00:00
/* Read the virtual counter. */
2012-03-05 11:49:31 +00:00
isb
2012-11-29 22:48:31 +00:00
mrs x15 , c n t v c t _ e l 0
2012-03-05 11:49:31 +00:00
/* Calculate cycle delta and convert to ns. */
2012-11-29 22:19:01 +00:00
sub x10 , x15 , x10
2012-03-05 11:49:31 +00:00
/* We can only guarantee 56 bits of precision. */
2012-11-29 22:19:01 +00:00
movn x15 , #0xff00 , l s l #48
and x10 , x15 , x10
mul x10 , x10 , x11
2012-03-05 11:49:31 +00:00
/* Use the kernel time to calculate the new timespec. */
2012-11-29 22:19:01 +00:00
mov x11 , #N S E C _ P E R _ S E C _ L O 16
movk x11 , #N S E C _ P E R _ S E C _ H I 16 , l s l #16
2012-11-29 22:33:29 +00:00
lsl x11 , x11 , x12
2012-11-29 22:19:01 +00:00
add x15 , x10 , x14
udiv x14 , x15 , x11
add x10 , x13 , x14
mul x13 , x14 , x11
sub x11 , x15 , x13
2012-03-05 11:49:31 +00:00
ret
.cfi_endproc
ENDPROC( _ _ d o _ g e t _ t s p e c )