1/*
2 *  linux/arch/x86_64/kernel/vsyscall.c
3 *
4 *  Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
5 *
6 *  Thanks to hpa@transmeta.com for some useful hint.
7 *  Special thanks to Ingo Molnar for his early experience with
8 *  a different vsyscall implementation for Linux/IA32 and for the name.
9 *
10 *  vsyscall 1 is located at -10Mbyte, vsyscall 2 is located
11 *  at virtual address -10Mbyte+1024bytes etc... There are at max 8192
12 *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
13 *  jumping out of line if necessary.
14 *
15 *  $Id: vsyscall.c,v 1.1.1.1 2008/10/15 03:26:21 james26_jang Exp $
16 */
17
18/*
19 * TODO 2001-03-20:
20 *
21 * 1) make page fault handler detect faults on page1-page-last of the vsyscall
22 *    virtual space, and make it increase %rip and write -ENOSYS in %rax (so
23 *    we'll be able to upgrade to a new glibc without upgrading kernel after
24 *    we add more vsyscalls.
25 * 2) Possibly we need a fixmap table for the vsyscalls too if we want
26 *    to avoid SIGSEGV and we want to return -EFAULT from the vsyscalls as well.
27 *    Can we segfault inside a "syscall"? We can fix this anytime and those fixes
28 *    won't be visible for userspace. Not fixing this is a noop for correct programs,
29 *    broken programs will segfault and there's no security risk until we choose to
30 *    fix it.
31 *
32 * These are not urgent things that we need to address only before shipping the first
33 * production binary kernels.
34 */
35
36#include <linux/time.h>
37#include <linux/init.h>
38#include <linux/kernel.h>
39#include <linux/mm.h>
40
41#include <asm/vsyscall.h>
42#include <asm/pgtable.h>
43#include <asm/page.h>
44#include <asm/fixmap.h>
45#include <asm/errno.h>
46#include <asm/io.h>
47#include <asm/msr.h>
48
49#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
50
51long __vxtime_sequence[2] __section_vxtime_sequence;
52
53#undef USE_VSYSCALL
54
55#ifdef USE_VSYSCALL
56
57static inline void do_vgettimeofday(struct timeval * tv)
58{
59	long sequence, t;
60	unsigned long sec, usec;
61
62	do {
63		sequence = __vxtime_sequence[1];
64		rmb();
65
66		rdtscll(t);
67		sec = __xtime.tv_sec;
68		usec = __xtime.tv_usec +
69			(__jiffies - __wall_jiffies) * (1000000 / HZ) +
70			(t  - __hpet.last_tsc) * (1000000 / HZ) / __hpet.ticks + __hpet.offset;
71
72		rmb();
73	} while (sequence != __vxtime_sequence[0]);
74
75	tv->tv_sec = sec + usec / 1000000;
76	tv->tv_usec = usec % 1000000;
77}
78
79static inline void do_get_tz(struct timezone * tz)
80{
81	long sequence;
82
83	do {
84		sequence = __vxtime_sequence[1];
85		rmb();
86
87		*tz = __sys_tz;
88
89		rmb();
90	} while (sequence != __vxtime_sequence[0]);
91}
92
93static long __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
94{
95	if (tv) do_vgettimeofday(tv);
96	if (tz) do_get_tz(tz);
97	return 0;
98}
99
100#else
101
102#include <asm/unistd.h>
103
104static long __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
105{
106	int r;
107	asm volatile("syscall"
108		     : "=a" (r)
109		     : "0" (__NR_gettimeofday), "D" (tv), "S" (tz)
110		     : "r11", "rcx","memory");
111	return r;
112}
113
114#endif
115
116static time_t __vsyscall(1) vtime(time_t * tp)
117{
118	struct timeval tv;
119	vgettimeofday(&tv, NULL);
120	if (tp) *tp = tv.tv_sec;
121	return tv.tv_sec;
122}
123
124static long __vsyscall(2) venosys_0(void)
125{
126	return -ENOSYS;
127}
128
129static long __vsyscall(3) venosys_1(void)
130{
131	return -ENOSYS;
132}
133
134static void __init map_vsyscall(void)
135{
136	extern char __vsyscall_0;
137	unsigned long physaddr_page0 = (unsigned long) &__vsyscall_0 - __START_KERNEL_map;
138
139	__set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
140	if (hpet.address)
141		__set_fixmap(VSYSCALL_HPET, hpet.address, PAGE_KERNEL_VSYSCALL);
142}
143
144static int __init vsyscall_init(void)
145{
146	printk("VSYSCALL: consistency checks...");
147	if ((unsigned long) &vgettimeofday != VSYSCALL_ADDR(__NR_vgettimeofday))
148		panic("vgettimeofday link addr broken");
149	if ((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime))
150		panic("vtime link addr broken");
151	if (VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE))
152		panic("fixmap first vsyscall %lx should be %lx", __fix_to_virt(VSYSCALL_FIRST_PAGE),
153			VSYSCALL_ADDR(0));
154	printk("passed...mapping...");
155	map_vsyscall();
156	printk("done.\n");
157
158	return 0;
159}
160
161__initcall(vsyscall_init);
162