vdso.c 6.0 KB
Newer Older
W
Will Deacon 已提交
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
/*
 * VDSO implementation for AArch64 and vector page setup for AArch32.
 *
 * 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/kernel.h>
#include <linux/clocksource.h>
#include <linux/elf.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/slab.h>
31
#include <linux/timekeeper_internal.h>
W
Will Deacon 已提交
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
#include <linux/vmalloc.h>

#include <asm/cacheflush.h>
#include <asm/signal32.h>
#include <asm/vdso.h>
#include <asm/vdso_datapage.h>

extern char vdso_start, vdso_end;
static unsigned long vdso_pages;
static struct page **vdso_pagelist;

/*
 * The vDSO data page.
 */
static union {
	struct vdso_data	data;
	u8			page[PAGE_SIZE];
} vdso_data_store __page_aligned_data;
struct vdso_data *vdso_data = &vdso_data_store.data;

#ifdef CONFIG_COMPAT
/*
 * Create and map the vectors page for AArch32 tasks.
 */
static struct page *vectors_page[1];

static int alloc_vectors_page(void)
{
	extern char __kuser_helper_start[], __kuser_helper_end[];
61 62
	extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];

W
Will Deacon 已提交
63
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;
64
	int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;
W
Will Deacon 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77
	unsigned long vpage;

	vpage = get_zeroed_page(GFP_ATOMIC);

	if (!vpage)
		return -ENOMEM;

	/* kuser helpers */
	memcpy((void *)vpage + 0x1000 - kuser_sz, __kuser_helper_start,
		kuser_sz);

	/* sigreturn code */
	memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET,
78
               __aarch32_sigret_code_start, sigret_sz);
W
Will Deacon 已提交
79 80 81 82 83 84 85 86 87 88 89 90

	flush_icache_range(vpage, vpage + PAGE_SIZE);
	vectors_page[0] = virt_to_page(vpage);

	return 0;
}
arch_initcall(alloc_vectors_page);

int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)
{
	struct mm_struct *mm = current->mm;
	unsigned long addr = AARCH32_VECTORS_BASE;
91 92 93 94 95 96
	static struct vm_special_mapping spec = {
		.name	= "[vectors]",
		.pages	= vectors_page,

	};
	void *ret;
W
Will Deacon 已提交
97

98 99
	if (down_write_killable(&mm->mmap_sem))
		return -EINTR;
W
Will Deacon 已提交
100 101 102
	current->mm->context.vdso = (void *)addr;

	/* Map vectors page at the high address. */
103 104 105
	ret = _install_special_mapping(mm, addr, PAGE_SIZE,
				       VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC,
				       &spec);
W
Will Deacon 已提交
106 107 108

	up_write(&mm->mmap_sem);

109
	return PTR_ERR_OR_ZERO(ret);
W
Will Deacon 已提交
110 111 112
}
#endif /* CONFIG_COMPAT */

113 114
static struct vm_special_mapping vdso_spec[2];

W
Will Deacon 已提交
115 116
static int __init vdso_init(void)
{
117 118 119 120 121 122
	int i;

	if (memcmp(&vdso_start, "\177ELF", 4)) {
		pr_err("vDSO is not a valid ELF object!\n");
		return -EINVAL;
	}
W
Will Deacon 已提交
123 124

	vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT;
125 126
	pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n",
		vdso_pages + 1, vdso_pages, &vdso_start, 1L, vdso_data);
W
Will Deacon 已提交
127 128

	/* Allocate the vDSO pagelist, plus a page for the data. */
129
	vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *),
W
Will Deacon 已提交
130
				GFP_KERNEL);
131
	if (vdso_pagelist == NULL)
W
Will Deacon 已提交
132 133
		return -ENOMEM;

134
	/* Grab the vDSO data page. */
135
	vdso_pagelist[0] = pfn_to_page(PHYS_PFN(__pa(vdso_data)));
136

W
Will Deacon 已提交
137
	/* Grab the vDSO code pages. */
138
	for (i = 0; i < vdso_pages; i++)
139
		vdso_pagelist[i + 1] = pfn_to_page(PHYS_PFN(__pa(&vdso_start)) + i);
W
Will Deacon 已提交
140

141 142
	/* Populate the special mapping structures */
	vdso_spec[0] = (struct vm_special_mapping) {
143
		.name	= "[vvar]",
144 145 146 147
		.pages	= vdso_pagelist,
	};

	vdso_spec[1] = (struct vm_special_mapping) {
148 149
		.name	= "[vdso]",
		.pages	= &vdso_pagelist[1],
150 151
	};

152
	return 0;
W
Will Deacon 已提交
153 154 155 156 157 158 159
}
arch_initcall(vdso_init);

int arch_setup_additional_pages(struct linux_binprm *bprm,
				int uses_interp)
{
	struct mm_struct *mm = current->mm;
160
	unsigned long vdso_base, vdso_text_len, vdso_mapping_len;
161
	void *ret;
W
Will Deacon 已提交
162

163
	vdso_text_len = vdso_pages << PAGE_SHIFT;
W
Will Deacon 已提交
164
	/* Be sure to map the data page */
165
	vdso_mapping_len = vdso_text_len + PAGE_SIZE;
W
Will Deacon 已提交
166

167 168
	if (down_write_killable(&mm->mmap_sem))
		return -EINTR;
W
Will Deacon 已提交
169 170
	vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
	if (IS_ERR_VALUE(vdso_base)) {
171
		ret = ERR_PTR(vdso_base);
W
Will Deacon 已提交
172 173
		goto up_fail;
	}
174 175
	ret = _install_special_mapping(mm, vdso_base, PAGE_SIZE,
				       VM_READ|VM_MAYREAD,
176 177
				       &vdso_spec[0]);
	if (IS_ERR(ret))
178 179
		goto up_fail;

180 181 182 183 184
	vdso_base += PAGE_SIZE;
	mm->context.vdso = (void *)vdso_base;
	ret = _install_special_mapping(mm, vdso_base, vdso_text_len,
				       VM_READ|VM_EXEC|
				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
185 186
				       &vdso_spec[1]);
	if (IS_ERR(ret))
W
Will Deacon 已提交
187 188
		goto up_fail;

189

W
Will Deacon 已提交
190
	up_write(&mm->mmap_sem);
191
	return 0;
W
Will Deacon 已提交
192

193 194 195
up_fail:
	mm->context.vdso = NULL;
	up_write(&mm->mmap_sem);
196
	return PTR_ERR(ret);
W
Will Deacon 已提交
197 198 199 200 201
}

/*
 * Update the vDSO data page to keep in sync with kernel timekeeping.
 */
202
void update_vsyscall(struct timekeeper *tk)
W
Will Deacon 已提交
203
{
204
	u32 use_syscall = strcmp(tk->tkr_mono.clock->name, "arch_sys_counter");
W
Will Deacon 已提交
205 206 207 208 209

	++vdso_data->tb_seq_count;
	smp_wmb();

	vdso_data->use_syscall			= use_syscall;
210 211 212
	vdso_data->xtime_coarse_sec		= tk->xtime_sec;
	vdso_data->xtime_coarse_nsec		= tk->tkr_mono.xtime_nsec >>
							tk->tkr_mono.shift;
213 214
	vdso_data->wtm_clock_sec		= tk->wall_to_monotonic.tv_sec;
	vdso_data->wtm_clock_nsec		= tk->wall_to_monotonic.tv_nsec;
W
Will Deacon 已提交
215 216

	if (!use_syscall) {
217
		vdso_data->cs_cycle_last	= tk->tkr_mono.cycle_last;
218
		vdso_data->xtime_clock_sec	= tk->xtime_sec;
219 220 221
		vdso_data->xtime_clock_nsec	= tk->tkr_mono.xtime_nsec;
		vdso_data->cs_mult		= tk->tkr_mono.mult;
		vdso_data->cs_shift		= tk->tkr_mono.shift;
W
Will Deacon 已提交
222 223 224 225 226 227 228 229 230 231 232
	}

	smp_wmb();
	++vdso_data->tb_seq_count;
}

void update_vsyscall_tz(void)
{
	vdso_data->tz_minuteswest	= sys_tz.tz_minuteswest;
	vdso_data->tz_dsttime		= sys_tz.tz_dsttime;
}