Skip to content

Commit c6b99be

Browse files
committed
LoongArch: Add VDSO and VSYSCALL support
Add VDSO and VSYSCALL support (sigreturn, gettimeofday and its friends) for LoongArch. Reviewed-by: WANG Xuerui <[email protected]> Reviewed-by: Jiaxun Yang <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent 559671e commit c6b99be

File tree

15 files changed

+622
-0
lines changed

15 files changed

+622
-0
lines changed

arch/loongarch/include/asm/vdso.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Author: Huacai Chen <[email protected]>
4+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5+
*/
6+
7+
#ifndef __ASM_VDSO_H
8+
#define __ASM_VDSO_H
9+
10+
#include <linux/mm_types.h>
11+
#include <vdso/datapage.h>
12+
13+
#include <asm/barrier.h>
14+
15+
/*
16+
* struct loongarch_vdso_info - Details of a VDSO image.
17+
* @vdso: Pointer to VDSO image (page-aligned).
18+
* @size: Size of the VDSO image (page-aligned).
19+
* @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline.
20+
* @code_mapping: Special mapping structure for vdso code.
21+
* @code_mapping: Special mapping structure for vdso data.
22+
*
23+
* This structure contains details of a VDSO image, including the image data
24+
* and offsets of certain symbols required by the kernel. It is generated as
25+
* part of the VDSO build process, aside from the mapping page array, which is
26+
* populated at runtime.
27+
*/
28+
struct loongarch_vdso_info {
29+
void *vdso;
30+
unsigned long size;
31+
unsigned long offset_sigreturn;
32+
struct vm_special_mapping code_mapping;
33+
struct vm_special_mapping data_mapping;
34+
};
35+
36+
extern struct loongarch_vdso_info vdso_info;
37+
38+
#endif /* __ASM_VDSO_H */
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
#ifndef __ASM_VDSOCLOCKSOURCE_H
3+
#define __ASM_VDSOCLOCKSOURCE_H
4+
5+
#define VDSO_ARCH_CLOCKMODES \
6+
VDSO_CLOCKMODE_CPU
7+
8+
#endif /* __ASM_VDSOCLOCKSOURCE_H */
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Author: Huacai Chen <[email protected]>
4+
*
5+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6+
*/
7+
#ifndef __ASM_VDSO_GETTIMEOFDAY_H
8+
#define __ASM_VDSO_GETTIMEOFDAY_H
9+
10+
#ifndef __ASSEMBLY__
11+
12+
#include <asm/unistd.h>
13+
#include <asm/vdso/vdso.h>
14+
15+
#define VDSO_HAS_CLOCK_GETRES 1
16+
17+
static __always_inline long gettimeofday_fallback(
18+
struct __kernel_old_timeval *_tv,
19+
struct timezone *_tz)
20+
{
21+
register struct __kernel_old_timeval *tv asm("a0") = _tv;
22+
register struct timezone *tz asm("a1") = _tz;
23+
register long nr asm("a7") = __NR_gettimeofday;
24+
register long ret asm("a0");
25+
26+
asm volatile(
27+
" syscall 0\n"
28+
: "+r" (ret)
29+
: "r" (nr), "r" (tv), "r" (tz)
30+
: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
31+
"$t8", "memory");
32+
33+
return ret;
34+
}
35+
36+
static __always_inline long clock_gettime_fallback(
37+
clockid_t _clkid,
38+
struct __kernel_timespec *_ts)
39+
{
40+
register clockid_t clkid asm("a0") = _clkid;
41+
register struct __kernel_timespec *ts asm("a1") = _ts;
42+
register long nr asm("a7") = __NR_clock_gettime;
43+
register long ret asm("a0");
44+
45+
asm volatile(
46+
" syscall 0\n"
47+
: "+r" (ret)
48+
: "r" (nr), "r" (clkid), "r" (ts)
49+
: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
50+
"$t8", "memory");
51+
52+
return ret;
53+
}
54+
55+
static __always_inline int clock_getres_fallback(
56+
clockid_t _clkid,
57+
struct __kernel_timespec *_ts)
58+
{
59+
register clockid_t clkid asm("a0") = _clkid;
60+
register struct __kernel_timespec *ts asm("a1") = _ts;
61+
register long nr asm("a7") = __NR_clock_getres;
62+
register long ret asm("a0");
63+
64+
asm volatile(
65+
" syscall 0\n"
66+
: "+r" (ret)
67+
: "r" (nr), "r" (clkid), "r" (ts)
68+
: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
69+
"$t8", "memory");
70+
71+
return ret;
72+
}
73+
74+
static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
75+
const struct vdso_data *vd)
76+
{
77+
uint64_t count;
78+
79+
__asm__ __volatile__(
80+
" rdtime.d %0, $zero\n"
81+
: "=r" (count));
82+
83+
return count;
84+
}
85+
86+
static inline bool loongarch_vdso_hres_capable(void)
87+
{
88+
return true;
89+
}
90+
#define __arch_vdso_hres_capable loongarch_vdso_hres_capable
91+
92+
static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
93+
{
94+
return get_vdso_data();
95+
}
96+
97+
#endif /* !__ASSEMBLY__ */
98+
99+
#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+
*/
5+
#ifndef __ASM_VDSO_PROCESSOR_H
6+
#define __ASM_VDSO_PROCESSOR_H
7+
8+
#ifndef __ASSEMBLY__
9+
10+
#define cpu_relax() barrier()
11+
12+
#endif /* __ASSEMBLY__ */
13+
14+
#endif /* __ASM_VDSO_PROCESSOR_H */
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
/*
3+
* Author: Huacai Chen <[email protected]>
4+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5+
*/
6+
7+
#ifndef __ASSEMBLY__
8+
9+
#include <asm/asm.h>
10+
#include <asm/page.h>
11+
12+
static inline unsigned long get_vdso_base(void)
13+
{
14+
unsigned long addr;
15+
16+
__asm__(
17+
" la.pcrel %0, _start\n"
18+
: "=r" (addr)
19+
:
20+
:);
21+
22+
return addr;
23+
}
24+
25+
static inline const struct vdso_data *get_vdso_data(void)
26+
{
27+
return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE);
28+
}
29+
30+
#endif /* __ASSEMBLY__ */
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __ASM_VDSO_VSYSCALL_H
3+
#define __ASM_VDSO_VSYSCALL_H
4+
5+
#ifndef __ASSEMBLY__
6+
7+
#include <linux/timekeeper_internal.h>
8+
#include <vdso/datapage.h>
9+
10+
extern struct vdso_data *vdso_data;
11+
12+
/*
13+
* Update the vDSO data page to keep in sync with kernel timekeeping.
14+
*/
15+
static __always_inline
16+
struct vdso_data *__loongarch_get_k_vdso_data(void)
17+
{
18+
return vdso_data;
19+
}
20+
#define __arch_get_k_vdso_data __loongarch_get_k_vdso_data
21+
22+
/* The asm-generic header needs to be included after the definitions above */
23+
#include <asm-generic/vdso/vsyscall.h>
24+
25+
#endif /* !__ASSEMBLY__ */
26+
27+
#endif /* __ASM_VDSO_VSYSCALL_H */

arch/loongarch/kernel/time.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ static struct clocksource clocksource_const = {
183183
.read = read_const_counter,
184184
.mask = CLOCKSOURCE_MASK(64),
185185
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
186+
.vdso_clock_mode = VDSO_CLOCKMODE_CPU,
186187
};
187188

188189
int __init constant_clocksource_init(void)

arch/loongarch/kernel/vdso.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Author: Huacai Chen <[email protected]>
4+
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5+
*/
6+
7+
#include <linux/binfmts.h>
8+
#include <linux/elf.h>
9+
#include <linux/err.h>
10+
#include <linux/init.h>
11+
#include <linux/ioport.h>
12+
#include <linux/kernel.h>
13+
#include <linux/mm.h>
14+
#include <linux/random.h>
15+
#include <linux/sched.h>
16+
#include <linux/slab.h>
17+
#include <linux/timekeeper_internal.h>
18+
19+
#include <asm/page.h>
20+
#include <asm/vdso.h>
21+
#include <vdso/helpers.h>
22+
#include <vdso/vsyscall.h>
23+
#include <generated/vdso-offsets.h>
24+
25+
extern char vdso_start[], vdso_end[];
26+
27+
/* Kernel-provided data used by the VDSO. */
28+
static union loongarch_vdso_data {
29+
u8 page[PAGE_SIZE];
30+
struct vdso_data data[CS_BASES];
31+
} loongarch_vdso_data __page_aligned_data;
32+
struct vdso_data *vdso_data = loongarch_vdso_data.data;
33+
static struct page *vdso_pages[] = { NULL };
34+
35+
static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
36+
{
37+
current->mm->context.vdso = (void *)(new_vma->vm_start);
38+
39+
return 0;
40+
}
41+
42+
struct loongarch_vdso_info vdso_info = {
43+
.vdso = vdso_start,
44+
.size = PAGE_SIZE,
45+
.code_mapping = {
46+
.name = "[vdso]",
47+
.pages = vdso_pages,
48+
.mremap = vdso_mremap,
49+
},
50+
.data_mapping = {
51+
.name = "[vvar]",
52+
},
53+
.offset_sigreturn = vdso_offset_sigreturn,
54+
};
55+
56+
static int __init init_vdso(void)
57+
{
58+
unsigned long i, pfn;
59+
60+
BUG_ON(!PAGE_ALIGNED(vdso_info.vdso));
61+
BUG_ON(!PAGE_ALIGNED(vdso_info.size));
62+
63+
pfn = __phys_to_pfn(__pa_symbol(vdso_info.vdso));
64+
for (i = 0; i < vdso_info.size / PAGE_SIZE; i++)
65+
vdso_info.code_mapping.pages[i] = pfn_to_page(pfn + i);
66+
67+
return 0;
68+
}
69+
subsys_initcall(init_vdso);
70+
71+
static unsigned long vdso_base(void)
72+
{
73+
unsigned long base = STACK_TOP;
74+
75+
if (current->flags & PF_RANDOMIZE) {
76+
base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
77+
base = PAGE_ALIGN(base);
78+
}
79+
80+
return base;
81+
}
82+
83+
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
84+
{
85+
int ret;
86+
unsigned long vvar_size, size, data_addr, vdso_addr;
87+
struct mm_struct *mm = current->mm;
88+
struct vm_area_struct *vma;
89+
struct loongarch_vdso_info *info = current->thread.vdso;
90+
91+
if (mmap_write_lock_killable(mm))
92+
return -EINTR;
93+
94+
/*
95+
* Determine total area size. This includes the VDSO data itself
96+
* and the data page.
97+
*/
98+
vvar_size = PAGE_SIZE;
99+
size = vvar_size + info->size;
100+
101+
data_addr = get_unmapped_area(NULL, vdso_base(), size, 0, 0);
102+
if (IS_ERR_VALUE(data_addr)) {
103+
ret = data_addr;
104+
goto out;
105+
}
106+
vdso_addr = data_addr + PAGE_SIZE;
107+
108+
vma = _install_special_mapping(mm, data_addr, vvar_size,
109+
VM_READ | VM_MAYREAD,
110+
&info->data_mapping);
111+
if (IS_ERR(vma)) {
112+
ret = PTR_ERR(vma);
113+
goto out;
114+
}
115+
116+
/* Map VDSO data page. */
117+
ret = remap_pfn_range(vma, data_addr,
118+
virt_to_phys(vdso_data) >> PAGE_SHIFT,
119+
PAGE_SIZE, PAGE_READONLY);
120+
if (ret)
121+
goto out;
122+
123+
/* Map VDSO code page. */
124+
vma = _install_special_mapping(mm, vdso_addr, info->size,
125+
VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
126+
&info->code_mapping);
127+
if (IS_ERR(vma)) {
128+
ret = PTR_ERR(vma);
129+
goto out;
130+
}
131+
132+
mm->context.vdso = (void *)vdso_addr;
133+
ret = 0;
134+
135+
out:
136+
mmap_write_unlock(mm);
137+
return ret;
138+
}

0 commit comments

Comments
 (0)