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