1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation;
5 * version 2.1 of the License.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
14 *
15 * Split out from xc_linus_osdep.c:
16 *
17 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
18 */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <stdio.h>
27
28 #include <sys/mman.h>
29 #include <sys/ioctl.h>
30
31 #include "private.h"
32
33 #ifndef O_CLOEXEC
34 #define O_CLOEXEC 0
35 #endif
36
osdep_xencall_open(xencall_handle * xcall)37 int osdep_xencall_open(xencall_handle *xcall)
38 {
39 int fd;
40
41 /*
42 * Prefer the newer interface.
43 */
44 fd = open("/dev/xen/privcmd", O_RDWR|O_CLOEXEC);
45
46 if ( fd == -1 && ( errno == ENOENT || errno == ENXIO || errno == ENODEV ))
47 {
48 /* Fallback to /proc/xen/privcmd */
49 fd = open("/proc/xen/privcmd", O_RDWR|O_CLOEXEC);
50 }
51
52 if ( fd == -1 )
53 {
54 PERROR("Could not obtain handle on privileged command interface");
55 return -1;
56 }
57
58 xcall->fd = fd;
59
60 /*
61 * Try the same for the hypercall buffer device.
62 */
63 fd = open("/dev/xen/hypercall", O_RDWR|O_CLOEXEC);
64 if ( fd == -1 && errno != ENOENT )
65 {
66 PERROR("Error on trying to open hypercall buffer device");
67 return -1;
68 }
69 xcall->buf_fd = fd;
70
71 return 0;
72 }
73
osdep_xencall_close(xencall_handle * xcall)74 int osdep_xencall_close(xencall_handle *xcall)
75 {
76 if ( xcall->buf_fd >= 0 )
77 close(xcall->buf_fd);
78 if ( xcall->fd >= 0 )
79 close(xcall->fd);
80 return 0;
81 }
82
osdep_hypercall(xencall_handle * xcall,privcmd_hypercall_t * hypercall)83 int osdep_hypercall(xencall_handle *xcall, privcmd_hypercall_t *hypercall)
84 {
85 return ioctl(xcall->fd, IOCTL_PRIVCMD_HYPERCALL, hypercall);
86 }
87
alloc_pages_bufdev(xencall_handle * xcall,size_t npages)88 static void *alloc_pages_bufdev(xencall_handle *xcall, size_t npages)
89 {
90 void *p;
91
92 p = mmap(NULL, npages * PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED,
93 xcall->buf_fd, 0);
94 if ( p == MAP_FAILED )
95 {
96 PERROR("alloc_pages: mmap (,%zu*%lu,...) [bufdev] failed",
97 npages, (unsigned long)PAGE_SIZE);
98 p = NULL;
99 }
100
101 return p;
102 }
103
alloc_pages_nobufdev(xencall_handle * xcall,size_t npages)104 static void *alloc_pages_nobufdev(xencall_handle *xcall, size_t npages)
105 {
106 size_t size = npages * PAGE_SIZE;
107 void *p;
108 int rc, i, saved_errno;
109
110 /* Address returned by mmap is page aligned. */
111 p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0);
112 if ( p == MAP_FAILED )
113 {
114 PERROR("alloc_pages: mmap(,%zu,...) [nobufdev] failed", size);
115 return NULL;
116 }
117
118 /* Do not copy the VMA to child process on fork. Avoid the page being COW
119 on hypercall. */
120 rc = madvise(p, npages * PAGE_SIZE, MADV_DONTFORK);
121 if ( rc < 0 )
122 {
123 PERROR("alloc_pages: madvise (,%zu*%lu,) [nobufdev] failed",
124 npages, (unsigned long)PAGE_SIZE);
125 goto out;
126 }
127
128 /*
129 * Touch each page in turn to force them to be un-CoWed, in case a
130 * fork happened in another thread at an inopportune moment
131 * above. The madvise() will prevent any subsequent fork calls from
132 * causing the same problem.
133 */
134 for ( i = 0; i < npages ; i++ )
135 {
136 char *c = (char *)p + (i*PAGE_SIZE);
137 *c = 0;
138 }
139
140 return p;
141
142 out:
143 saved_errno = errno;
144 (void)munmap(p, size);
145 errno = saved_errno;
146 return NULL;
147 }
148
osdep_alloc_pages(xencall_handle * xcall,size_t npages)149 void *osdep_alloc_pages(xencall_handle *xcall, size_t npages)
150 {
151 void *p;
152
153 if ( xcall->buf_fd >= 0 )
154 p = alloc_pages_bufdev(xcall, npages);
155 else
156 p = alloc_pages_nobufdev(xcall, npages);
157
158 return p;
159 }
160
osdep_free_pages(xencall_handle * xcall,void * ptr,size_t npages)161 void osdep_free_pages(xencall_handle *xcall, void *ptr, size_t npages)
162 {
163 int saved_errno = errno;
164
165 if ( xcall->buf_fd < 0 )
166 {
167 /* Recover the VMA flags. Maybe it's not necessary */
168 madvise(ptr, npages * PAGE_SIZE, MADV_DOFORK);
169 }
170
171 munmap(ptr, npages * PAGE_SIZE);
172 /* We MUST propagate the hypercall errno, not unmap call's. */
173 errno = saved_errno;
174 }
175
xencall_buffers_never_fault(xencall_handle * xcall)176 int xencall_buffers_never_fault(xencall_handle *xcall)
177 {
178 return xcall->buf_fd >= 0;
179 }
180
181 /*
182 * Local variables:
183 * mode: C
184 * c-file-style: "BSD"
185 * c-basic-offset: 4
186 * tab-width: 4
187 * indent-tabs-mode: nil
188 * End:
189 */
190