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 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 %%eax; push %%ebx; push %%ecx; push %%edx\n\t"
56 "test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
57 "mov %%eax,(%2); mov %%ebx,4(%2)\n\t"
58 "mov %%ecx,8(%2); mov %%edx,12(%2)\n\t"
59 "pop %%edx; pop %%ecx; pop %%ebx; pop %%eax\n\t"
60 : : "a" (idx), "c" (pv_context), "S" (regs) : "memory" );
61 #else
62 asm volatile (
63 "test %5,%5 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
64 : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
65 : "0" (idx), "1" (pv_context), "2" (0) );
66 #endif
67 }
68
check_for_xen(int pv_context)69 static int check_for_xen(int pv_context)
70 {
71 uint32_t regs[4];
72 char signature[13];
73 uint32_t base;
74
75 for ( base = 0x40000000; base < 0x40010000; base += 0x100 )
76 {
77 cpuid(base, regs, pv_context);
78
79 *(uint32_t *)(signature + 0) = regs[1];
80 *(uint32_t *)(signature + 4) = regs[2];
81 *(uint32_t *)(signature + 8) = regs[3];
82 signature[12] = '\0';
83
84 if ( !strcmp("XenVMMXenVMM", signature) && (regs[0] >= (base + 2)) )
85 goto found;
86 }
87
88 return 0;
89
90 found:
91 cpuid(base + 1, regs, pv_context);
92 if ( regs[0] )
93 {
94 int r = asprintf(&ver, "V%u.%u", (uint16_t)(regs[0] >> 16),
95 (uint16_t)regs[0]);
96 if ( r < 0 )
97 {
98 perror("asprintf failed\n");
99 exit(EXIT_FAILURE);
100 }
101 }
102 return regs[0];
103 }
104
105 static jmp_buf sigill_jmp;
sigill_handler(int sig)106 void sigill_handler(int sig)
107 {
108 longjmp(sigill_jmp, 1);
109 }
110
usage(void)111 static void usage(void)
112 {
113 printf("Usage: xen_detect [options]\n");
114 printf("Options:\n");
115 printf(" -h, --help Display this information\n");
116 printf(" -q, --quiet Quiesce normal informational output\n");
117 printf(" -P, --pv Exit status 1 if not running as PV guest\n");
118 printf(" -H, --hvm Exit status 1 if not running as HVM or PVH guest.\n");
119 printf(" -N, --none Exit status 1 if running on Xen (PV or HVM)\n");
120 }
121
check_dir(const char * filename)122 static bool check_dir(const char *filename)
123 {
124 FILE *f;
125 struct stat stab;
126 bool res;
127
128 f = fopen(filename, "r");
129 if ( !f )
130 return false;
131 res = !fstat(fileno(f), &stab) && S_ISDIR(stab.st_mode);
132 fclose(f);
133
134 return res;
135 }
136
read_file_content(const char * filename)137 static char *read_file_content(const char *filename)
138 {
139 FILE *f;
140 struct stat stab;
141 char *content = NULL;
142 int datalen;
143
144 f = fopen(filename, "r");
145 if ( !f )
146 return NULL;
147
148 if ( fstat(fileno(f), &stab) || !S_ISREG(stab.st_mode) ||
149 stab.st_size > INT_MAX || !stab.st_size )
150 goto out;
151
152 content = malloc(stab.st_size + 1);
153 if ( !content )
154 goto out;
155
156 /* For sysfs file, datalen is always PAGE_SIZE. 'read'
157 * will return the number of bytes of the actual content,
158 * rs <= datalen is expected.
159 */
160 datalen = fread(content, 1, stab.st_size, f);
161 content[datalen] = 0;
162 if ( ferror(f) )
163 {
164 free(content);
165 content = NULL;
166 }
167
168 out:
169 fclose(f);
170 return content;
171 }
172
check_sysfs(void)173 static enum guest_type check_sysfs(void)
174 {
175 char *str, *tmp;
176 enum guest_type res = XEN_NONE;
177
178 if ( !check_dir("/sys/hypervisor") )
179 return 0;
180
181 str = read_file_content("/sys/hypervisor/type");
182 if ( !str || strcmp(str, "xen\n") )
183 goto out;
184 free(str);
185
186 str = read_file_content("/sys/hypervisor/guest_type");
187 if ( !str )
188 return 0;
189 str[strlen(str) - 1] = 0;
190 type = str;
191 if ( !strcmp(type, "PV") )
192 res = XEN_PV;
193 else
194 res = XEN_HVM;
195
196 str = read_file_content("/sys/hypervisor/version/major");
197 if ( str )
198 str[strlen(str) - 1] = 0;
199 tmp = read_file_content("/sys/hypervisor/version/minor");
200 if ( tmp )
201 tmp[strlen(tmp) - 1] = 0;
202 if ( str && tmp )
203 {
204 int r = asprintf(&ver, "V%s.%s", str, tmp);
205 if ( r < 0 )
206 {
207 perror("asprintf failed\n");
208 exit(EXIT_FAILURE);
209 }
210 } else
211 ver = strdup("unknown version");
212 free(tmp);
213
214 out:
215 free(str);
216 return res;
217 }
218
main(int argc,char ** argv)219 int main(int argc, char **argv)
220 {
221 enum guest_type detected, expected = 0;
222 int ch, quiet = 0;
223
224 const static char sopts[] = "hqPHN";
225 const static struct option lopts[] = {
226 { "help", 0, NULL, 'h' },
227 { "quiet", 0, NULL, 'q' },
228 { "pv", 0, NULL, 'P' },
229 { "hvm", 0, NULL, 'H' },
230 { "none", 0, NULL, 'N' },
231 { 0, 0, 0, 0}
232 };
233
234 while ( (ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1 )
235 {
236 switch ( ch )
237 {
238 case 'q':
239 quiet = 1;
240 break;
241 case 'P':
242 expected = XEN_PV;
243 break;
244 case 'H':
245 expected = XEN_HVM;
246 break;
247 case 'N':
248 expected = XEN_NONE;
249 break;
250 default:
251 usage();
252 exit(1);
253 }
254 }
255
256 detected = check_sysfs();
257 if ( detected )
258 goto out;
259
260 /* Check for execution in HVM context. */
261 detected = XEN_HVM;
262 type = "HVM";
263 if ( check_for_xen(0) )
264 goto out;
265
266 /*
267 * Set up a signal handler to test the paravirtualised CPUID instruction.
268 * If executed outside Xen PV context, the extended opcode will fault, we
269 * will longjmp via the signal handler, and print "Not running on Xen".
270 */
271 detected = XEN_PV;
272 type = "PV";
273 if ( !setjmp(sigill_jmp)
274 && (signal(SIGILL, sigill_handler) != SIG_ERR)
275 && check_for_xen(1) )
276 goto out;
277
278 detected = XEN_NONE;
279
280 out:
281 if ( quiet )
282 /* nothing */;
283 else if ( detected == XEN_NONE )
284 printf("Not running on Xen.\n");
285 else
286 printf("Running in %s context on Xen %s.\n", type, ver);
287
288 free(ver);
289
290 return expected && (expected != detected);
291 }
292