Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 25 additions & 34 deletions arch/mips/include/asm/vdso/gettimeofday.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,40 @@ static __always_inline u64 read_gic_count(const struct vdso_time_data *data)

#endif

/*
* HWR $30 (Loongson64 constant timer) is 64 bits wide. RDHWR never sign-extends
* the timer count before copying it into a GPR, as the kernel always sets
* Status.UX=1 even in O32 userspace. If it's kept as is, the "shadow" on the
* upper half breaks subsequent logical/branch instructions relying on the GPR.
*
* Thankfully, Status.UX==1 also means that we have the full timer count
* available and can use 64-bit instructions to salvage it into a pair of GPR
* to comply with O32 ABI.
*/
#ifdef CONFIG_CPU_LOONGSON64
static __always_inline u64 read_const_count(void)
{
unsigned long count;
u64 count;

/* N32 ABI stores 64-bit value in a single register. */
#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
__asm__ __volatile__(
" .set push\n"
" .set mips32r2\n"
" rdhwr %0, $30\n"
" .set pop\n"
: "=r" (count));
#else /* _MIPS_SIM == _MIPS_SIM_ABI32 */
__asm__ __volatile__(
" .set push\n"
" .set mips64r2\n" /* Enable 64-bit instructions. */
" .set noreorder\n"
" rdhwr %0, $30\n" /* Now %0 holds the full 64-bit count. */
" dsra %D0, %0, 32\n" /* Salvage and sign-extend the upper half. */
" sll %0, %0, 0\n" /* Sign-extend the lower half. */
" .set pop\n"
: "=r" (count));
#endif

return count;
}
Expand Down Expand Up @@ -225,42 +248,10 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
static inline bool mips_vdso_hres_capable(void)
{
return IS_ENABLED(CONFIG_CSRC_R4K) ||
IS_ENABLED(CONFIG_CLKSRC_MIPS_GIC) ||
IS_ENABLED(CONFIG_CPU_LOONGSON64);
IS_ENABLED(CONFIG_CLKSRC_MIPS_GIC);
}
#define __arch_vdso_hres_capable mips_vdso_hres_capable

/*
* HWR $30 (Loongson64 constant timer) is 64 bits wide. However, some hardware
* doesn't properly sign-extend its value before copying it into a GPR in o32
* mode (Status.UX==1). This "shadow" on the higher half breaks most subsequent
* instructions relying on the GPR.
*
* Aside from the HW bug, it is never possible to retrieve the higher half of
* the constant timer in o32 mode, which implies that the masks of timer cycles
* differentiate in o32 versus n32/n64 modes. Considering that a clocksource can
* only have a single mask, we are unable to make the o32 vDSO parse the timer
* delta properly anyway.
*
* Moreover, the timer frequency is simply too high that 32-bit timer cycles
* wrap crazily -- it's fundamentally unreliable without the higher half.
*
* Let's prevent o32 vDSO from using the constant timer.
*
* Note: the likely() here is to prevent GCC from emitting calls to libgcc.
* Despite that, the hint itself also tells the truth.
*/
#if defined(CONFIG_CPU_LOONGSON64) && _MIPS_SIM == _MIPS_SIM_ABI32

static inline bool mips_vdso_clocksource_ok(const struct vdso_clock *vc)
{
return (likely(vc->clock_mode != VDSO_CLOCKMODE_NONE) &&
vc->clock_mode != VDSO_CLOCKMODE_CONST);
}
#define vdso_clocksource_ok mips_vdso_clocksource_ok

#endif /* defined(CONFIG_CPU_LOONGSON64) && _MIPS_SIM == _MIPS_SIM_ABI32 */

static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void)
{
return get_vdso_time_data();
Expand Down