1 /* SPDX-License-Identifier: LGPL-2.1 */
2 /*
3  * xc_version.c
4  *
5  * Wrappers aound XENVER_* hypercalls
6  */
7 
8 #include "xc_private.h"
9 #include <assert.h>
10 
do_xen_version(xc_interface * xch,int cmd,xc_hypercall_buffer_t * dest)11 static int do_xen_version(xc_interface *xch, int cmd,
12                           xc_hypercall_buffer_t *dest)
13 {
14     DECLARE_HYPERCALL_BUFFER_ARGUMENT(dest);
15     return xencall2(xch->xcall, __HYPERVISOR_xen_version,
16                     cmd, HYPERCALL_BUFFER_AS_ARG(dest));
17 }
18 
xc_version(xc_interface * xch,int cmd,void * arg)19 int xc_version(xc_interface *xch, int cmd, void *arg)
20 {
21     DECLARE_HYPERCALL_BOUNCE(arg, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); /* Size unknown until cmd decoded */
22     size_t sz;
23     int rc;
24 
25     switch ( cmd )
26     {
27     case XENVER_version:
28         sz = 0;
29         break;
30     case XENVER_extraversion:
31         sz = sizeof(xen_extraversion_t);
32         break;
33     case XENVER_compile_info:
34         sz = sizeof(xen_compile_info_t);
35         break;
36     case XENVER_capabilities:
37         sz = sizeof(xen_capabilities_info_t);
38         break;
39     case XENVER_changeset:
40         sz = sizeof(xen_changeset_info_t);
41         break;
42     case XENVER_platform_parameters:
43         sz = sizeof(xen_platform_parameters_t);
44         break;
45     case XENVER_get_features:
46         sz = sizeof(xen_feature_info_t);
47         break;
48     case XENVER_pagesize:
49         sz = 0;
50         break;
51     case XENVER_guest_handle:
52         sz = sizeof(xen_domain_handle_t);
53         break;
54     case XENVER_commandline:
55         sz = sizeof(xen_commandline_t);
56         break;
57     case XENVER_build_id:
58         {
59             xen_build_id_t *build_id = (xen_build_id_t *)arg;
60             sz = sizeof(*build_id) + build_id->len;
61             HYPERCALL_BOUNCE_SET_DIR(arg, XC_HYPERCALL_BUFFER_BOUNCE_BOTH);
62             break;
63         }
64     default:
65         ERROR("xc_version: unknown command %d\n", cmd);
66         return -EINVAL;
67     }
68 
69     HYPERCALL_BOUNCE_SET_SIZE(arg, sz);
70 
71     if ( (sz != 0) && xc_hypercall_bounce_pre(xch, arg) )
72     {
73         PERROR("Could not bounce buffer for version hypercall");
74         return -ENOMEM;
75     }
76 
77     rc = do_xen_version(xch, cmd, HYPERCALL_BUFFER(arg));
78 
79     if ( sz != 0 )
80         xc_hypercall_bounce_post(xch, arg);
81 
82     return rc;
83 }
84 
85 /*
86  * Raw hypercall wrapper, letting us pass NULL and things which aren't of
87  * xc_hypercall_buffer_t *.
88  */
do_xen_version_raw(xc_interface * xch,int cmd,void * hbuf)89 static int do_xen_version_raw(xc_interface *xch, int cmd, void *hbuf)
90 {
91     return xencall2(xch->xcall, __HYPERVISOR_xen_version,
92                     cmd, (unsigned long)hbuf);
93 }
94 
95 /*
96  * Issues a xen_varbuf_t subop, using manual hypercall buffer handling to
97  * avoid unnecessary buffering.
98  *
99  * On failure, returns NULL.  errno probably useful.
100  * On success, returns a pointer which must be freed with xencall_free_buffer().
101  */
varbuf_op(xc_interface * xch,unsigned int subop)102 static xen_varbuf_t *varbuf_op(xc_interface *xch, unsigned int subop)
103 {
104     xen_varbuf_t *hbuf = NULL;
105     ssize_t sz;
106 
107     sz = do_xen_version_raw(xch, subop, NULL);
108     if ( sz < 0 )
109         return NULL;
110 
111     hbuf = xencall_alloc_buffer(xch->xcall, sizeof(*hbuf) + sz);
112     if ( !hbuf )
113         return NULL;
114 
115     hbuf->len = sz;
116 
117     sz = do_xen_version_raw(xch, subop, hbuf);
118     if ( sz < 0 )
119     {
120         xencall_free_buffer(xch->xcall, hbuf);
121         return NULL;
122     }
123 
124     hbuf->len = sz;
125     return hbuf;
126 }
127 
128 /*
129  * Wrap varbuf_op() to obtain a simple string.  Copy out of the hypercall
130  * buffer, stripping the xen_varbuf_t header and appending a NUL terminator.
131  *
132  * On failure, returns NULL, errno probably useful.
133  * On success, returns a pointer which must be free()'d.
134  */
varbuf_simple_string(xc_interface * xch,unsigned int subop)135 static char *varbuf_simple_string(xc_interface *xch, unsigned int subop)
136 {
137     xen_varbuf_t *hbuf = varbuf_op(xch, subop);
138     char *res;
139 
140     if ( !hbuf )
141         return NULL;
142 
143     res = malloc(hbuf->len + 1);
144     if ( res )
145     {
146         memcpy(res, hbuf->buf, hbuf->len);
147         res[hbuf->len] = '\0';
148     }
149 
150     xencall_free_buffer(xch->xcall, hbuf);
151 
152     return res;
153 }
154 
xc_xenver_extraversion(xc_interface * xch)155 char *xc_xenver_extraversion(xc_interface *xch)
156 {
157     return varbuf_simple_string(xch, XENVER_extraversion2);
158 }
159 
xc_xenver_capabilities(xc_interface * xch)160 char *xc_xenver_capabilities(xc_interface *xch)
161 {
162     return varbuf_simple_string(xch, XENVER_capabilities2);
163 }
164 
xc_xenver_changeset(xc_interface * xch)165 char *xc_xenver_changeset(xc_interface *xch)
166 {
167     return varbuf_simple_string(xch, XENVER_changeset2);
168 }
169 
xc_xenver_commandline(xc_interface * xch)170 char *xc_xenver_commandline(xc_interface *xch)
171 {
172     return varbuf_simple_string(xch, XENVER_commandline2);
173 }
174 
str2hex(char * dst,const unsigned char * src,size_t n)175 static void str2hex(char *dst, const unsigned char *src, size_t n)
176 {
177     static const unsigned char hex[] = "0123456789abcdef";
178 
179     for ( ; n; n-- )
180     {
181         unsigned char c = *src++;
182 
183         *dst++ = hex[c >> 4];
184         *dst++ = hex[c & 0xf];
185     }
186 }
187 
xc_xenver_buildid(xc_interface * xch)188 char *xc_xenver_buildid(xc_interface *xch)
189 {
190     xen_varbuf_t *hbuf = varbuf_op(xch, XENVER_build_id);
191     char *res;
192 
193     if ( !hbuf )
194         return NULL;
195 
196     res = malloc((hbuf->len * 2) + 1);
197     if ( res )
198     {
199         str2hex(res, hbuf->buf, hbuf->len);
200         res[hbuf->len * 2] = '\0';
201     }
202 
203     xencall_free_buffer(xch->xcall, hbuf);
204 
205     return res;
206 }
207