summaryrefslogtreecommitdiffstats
path: root/kernel/arch/mips/include/asm/vdso.h
blob: 8f4ca5dd992b1e8ed28791488c1ebe8403f80d7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (C) 2015 Imagination Technologies
 * Author: Alex Smith <alex.smith@imgtec.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#ifndef __ASM_VDSO_H
#define __ASM_VDSO_H

#include <linux/mm_types.h>

#include <asm/barrier.h>

/**
 * struct mips_vdso_image - Details of a VDSO image.
 * @data: Pointer to VDSO image data (page-aligned).
 * @size: Size of the VDSO image data (page-aligned).
 * @off_sigreturn: Offset of the sigreturn() trampoline.
 * @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline.
 * @mapping: Special mapping structure.
 *
 * This structure contains details of a VDSO image, including the image data
 * and offsets of certain symbols required by the kernel. It is generated as
 * part of the VDSO build process, aside from the mapping page array, which is
 * populated at runtime.
 */
struct mips_vdso_image {
	void *data;
	unsigned long size;

	unsigned long off_sigreturn;
	unsigned long off_rt_sigreturn;

	struct vm_special_mapping mapping;
};

/*
 * The following structures are auto-generated as part of the build for each
 * ABI by genvdso, see arch/mips/vdso/Makefile.
 */

extern struct mips_vdso_image vdso_image;

#ifdef CONFIG_MIPS32_O32
extern struct mips_vdso_image vdso_image_o32;
#endif

#ifdef CONFIG_MIPS32_N32
extern struct mips_vdso_image vdso_image_n32;
#endif

/**
 * union mips_vdso_data - Data provided by the kernel for the VDSO.
 * @xtime_sec:		Current real time (seconds part).
 * @xtime_nsec:		Current real time (nanoseconds part, shifted).
 * @wall_to_mono_sec:	Wall-to-monotonic offset (seconds part).
 * @wall_to_mono_nsec:	Wall-to-monotonic offset (nanoseconds part).
 * @seq_count:		Counter to synchronise updates (odd = updating).
 * @cs_shift:		Clocksource shift value.
 * @clock_mode:		Clocksource to use for time functions.
 * @cs_mult:		Clocksource multiplier value.
 * @cs_cycle_last:	Clock cycle value at last update.
 * @cs_mask:		Clocksource mask value.
 * @tz_minuteswest:	Minutes west of Greenwich (from timezone).
 * @tz_dsttime:		Type of DST correction (from timezone).
 *
 * This structure contains data needed by functions within the VDSO. It is
 * populated by the kernel and mapped read-only into user memory. The time
 * fields are mirrors of internal data from the timekeeping infrastructure.
 *
 * Note: Care should be taken when modifying as the layout must remain the same
 * for both 64- and 32-bit (for 32-bit userland on 64-bit kernel).
 */
union mips_vdso_data {
	struct {
		u64 xtime_sec;
		u64 xtime_nsec;
		u32 wall_to_mono_sec;
		u32 wall_to_mono_nsec;
		u32 seq_count;
		u32 cs_shift;
		u8 clock_mode;
		u32 cs_mult;
		u64 cs_cycle_last;
		u64 cs_mask;
		s32 tz_minuteswest;
		s32 tz_dsttime;
	};

	u8 page[PAGE_SIZE];
};

static inline u32 vdso_data_read_begin(const union mips_vdso_data *data)
{
	u32 seq;

	while (true) {
		seq = ACCESS_ONCE(data->seq_count);
		if (likely(!(seq & 1))) {
			/* Paired with smp_wmb() in vdso_data_write_*(). */
			smp_rmb();
			return seq;
		}

		cpu_relax();
	}
}

static inline bool vdso_data_read_retry(const union mips_vdso_data *data,
					u32 start_seq)
{
	/* Paired with smp_wmb() in vdso_data_write_*(). */
	smp_rmb();
	return unlikely(data->seq_count != start_seq);
}

static inline void vdso_data_write_begin(union mips_vdso_data *data)
{
	++data->seq_count;

	/* Ensure sequence update is written before other data page values. */
	smp_wmb();
}

static inline void vdso_data_write_end(union mips_vdso_data *data)
{
	/* Ensure data values are written before updating sequence again. */
	smp_wmb();
	++data->seq_count;
}

#endif /* __ASM_VDSO_H */