1 /******************************************************************************
2 * xen_detect.c
3 *
4 * Simple GNU C / POSIX application to detect execution on Xen VMM platform.
5 *
6 * Copyright (c) 2007, XenSource Inc.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 */
26
27 #define _GNU_SOURCE
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <limits.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <setjmp.h>
37 #include <signal.h>
38 #include <unistd.h>
39 #include <getopt.h>
40
41 enum guest_type {
42 XEN_PV = 1,
43 XEN_HVM = 2,
44 XEN_NONE = 3
45 };
46
47 static const char *type;
48 static char *ver;
49
cpuid(uint32_t idx,uint32_t * regs,int pv_context)50 static void cpuid(uint32_t idx, uint32_t *regs, int pv_context)
51 {
52 #ifdef __i386__
53 /* Use the stack to avoid reg constraint failures with some gcc flags */
54 asm volatile (
55 "push %%ebx; push %%edx\n\t"
56 "test %[pv],%[pv] ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
57 "mov %%eax,(%[regs]); mov %%ebx,4(%[regs])\n\t"
58 "mov %%ecx,8(%[regs]); mov %%edx,12(%[regs])\n\t"
59 "pop %%edx; pop %%ebx\n\t"
60 : "+a" (idx), "=c" (idx /* dummy */)
61 : "c" (0), [pv] "r" (pv_context), [regs] "SD" (regs)
62 : "memory" );
63 #else
64 asm volatile (
65 "test %[pv],%[pv] ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
66 : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
67 : "0" (idx), "2" (0), [pv] "r" (pv_context) );
68 #endif
69 }
70
check_for_xen(int pv_context)71 static int check_for_xen(int pv_context)
72 {
73 uint32_t regs[4];
74 char signature[13];
75 uint32_t base;
76
77 for ( base = 0x40000000; base < 0x40010000; base += 0x100 )
78 {
79 cpuid(base, regs, pv_context);
80
81 *(uint32_t *)(signature + 0) = regs[1];
82 *(uint32_t *)(signature + 4) = regs[2];
83 *(uint32_t *)(signature + 8) = regs[3];
84 signature[12] = '\0';
85
86 if ( !strcmp("XenVMMXenVMM", signature) && (regs[0] >= (base + 2)) )
87 goto found;
88
89 /* Higher base addresses are possible only with HVM. */
90 if ( pv_context )
91 break;
92 }
93
94 return 0;
95
96 found:
97 /*
98 * On CPUID faulting capable hardware even un-escaped CPUID will return
99 * the hypervisor leaves. Need to further distinguish modes.
100 */
101 if ( !pv_context )
102 {
103 /*
104 * XEN_CPUID_FEAT1_MMU_PT_UPDATE_PRESERVE_AD is a PV-only feature
105 * pre-dating CPUID faulting support in Xen. Hence we can use it to
106 * tell whether we shouldn't report "success" to our caller here.
107 */
108 cpuid(base + 2, regs, 0);
109 if ( regs[2] & (1u << 0) )
110 return 0;
111 }
112
113 cpuid(base + 1, regs, pv_context);
114 if ( regs[0] )
115 {
116 int r = asprintf(&ver, "V%u.%u", (uint16_t)(regs[0] >> 16),
117 (uint16_t)regs[0]);
118 if ( r < 0 )
119 {
120 perror("asprintf failed\n");
121 exit(EXIT_FAILURE);
122 }
123 }
124 return regs[0];
125 }
126
127 static jmp_buf sigill_jmp;
sigill_handler(int sig)128 void sigill_handler(int sig)
129 {
130 longjmp(sigill_jmp, 1);
131 }
132
usage(void)133 static void usage(void)
134 {
135 printf("Usage: xen_detect [options]\n");
136 printf("Options:\n");
137 printf(" -h, --help Display this information\n");
138 printf(" -q, --quiet Quiesce normal informational output\n");
139 printf(" -P, --pv Exit status 1 if not running as PV guest\n");
140 printf(" -H, --hvm Exit status 1 if not running as HVM or PVH guest.\n");
141 printf(" -N, --none Exit status 1 if running on Xen (PV or HVM)\n");
142 }
143
check_dir(const char * filename)144 static bool check_dir(const char *filename)
145 {
146 FILE *f;
147 struct stat stab;
148 bool res;
149
150 f = fopen(filename, "r");
151 if ( !f )
152 return false;
153 res = !fstat(fileno(f), &stab) && S_ISDIR(stab.st_mode);
154 fclose(f);
155
156 return res;
157 }
158
read_file_content(const char * filename)159 static char *read_file_content(const char *filename)
160 {
161 FILE *f;
162 struct stat stab;
163 char *content = NULL;
164 int datalen;
165
166 f = fopen(filename, "r");
167 if ( !f )
168 return NULL;
169
170 if ( fstat(fileno(f), &stab) || !S_ISREG(stab.st_mode) ||
171 stab.st_size > INT_MAX || !stab.st_size )
172 goto out;
173
174 content = malloc(stab.st_size + 1);
175 if ( !content )
176 goto out;
177
178 /* For sysfs file, datalen is always PAGE_SIZE. 'read'
179 * will return the number of bytes of the actual content,
180 * rs <= datalen is expected.
181 */
182 datalen = fread(content, 1, stab.st_size, f);
183 content[datalen] = 0;
184 if ( ferror(f) )
185 {
186 free(content);
187 content = NULL;
188 }
189
190 out:
191 fclose(f);
192 return content;
193 }
194
check_sysfs(void)195 static enum guest_type check_sysfs(void)
196 {
197 char *str, *tmp;
198 enum guest_type res = XEN_NONE;
199
200 if ( !check_dir("/sys/hypervisor") )
201 return 0;
202
203 str = read_file_content("/sys/hypervisor/type");
204 if ( !str || strcmp(str, "xen\n") )
205 goto out;
206 free(str);
207
208 str = read_file_content("/sys/hypervisor/guest_type");
209 if ( !str )
210 return 0;
211 str[strlen(str) - 1] = 0;
212 type = str;
213 if ( !strcmp(type, "PV") )
214 res = XEN_PV;
215 else
216 res = XEN_HVM;
217
218 str = read_file_content("/sys/hypervisor/version/major");
219 if ( str )
220 str[strlen(str) - 1] = 0;
221 tmp = read_file_content("/sys/hypervisor/version/minor");
222 if ( tmp )
223 tmp[strlen(tmp) - 1] = 0;
224 if ( str && tmp )
225 {
226 int r = asprintf(&ver, "V%s.%s", str, tmp);
227 if ( r < 0 )
228 {
229 perror("asprintf failed\n");
230 exit(EXIT_FAILURE);
231 }
232 } else
233 ver = strdup("unknown version");
234 free(tmp);
235
236 out:
237 free(str);
238 return res;
239 }
240
main(int argc,char ** argv)241 int main(int argc, char **argv)
242 {
243 enum guest_type detected, expected = 0;
244 int ch, quiet = 0;
245
246 const static char sopts[] = "hqPHN";
247 const static struct option lopts[] = {
248 { "help", 0, NULL, 'h' },
249 { "quiet", 0, NULL, 'q' },
250 { "pv", 0, NULL, 'P' },
251 { "hvm", 0, NULL, 'H' },
252 { "none", 0, NULL, 'N' },
253 { 0, 0, 0, 0}
254 };
255
256 while ( (ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1 )
257 {
258 switch ( ch )
259 {
260 case 'q':
261 quiet = 1;
262 break;
263 case 'P':
264 expected = XEN_PV;
265 break;
266 case 'H':
267 expected = XEN_HVM;
268 break;
269 case 'N':
270 expected = XEN_NONE;
271 break;
272 default:
273 usage();
274 exit(1);
275 }
276 }
277
278 detected = check_sysfs();
279 if ( detected )
280 goto out;
281
282 /* Check for execution in HVM context. */
283 detected = XEN_HVM;
284 type = "HVM";
285 if ( check_for_xen(0) )
286 goto out;
287
288 /*
289 * Set up a signal handler to test the paravirtualised CPUID instruction.
290 * If executed outside Xen PV context, the extended opcode will fault, we
291 * will longjmp via the signal handler, and print "Not running on Xen".
292 */
293 detected = XEN_PV;
294 type = "PV";
295 if ( !setjmp(sigill_jmp)
296 && (signal(SIGILL, sigill_handler) != SIG_ERR)
297 && check_for_xen(1) )
298 goto out;
299
300 detected = XEN_NONE;
301
302 out:
303 if ( quiet )
304 /* nothing */;
305 else if ( detected == XEN_NONE )
306 printf("Not running on Xen.\n");
307 else
308 printf("Running in %s context on Xen %s.\n", type, ver);
309
310 free(ver);
311
312 return expected && (expected != detected);
313 }
314