1 #include <stdbool.h>
2 #include <syslog.h>
3 #include <stdio.h>
4 #include <err.h>
5 #include <stdlib.h>
6 #include <sys/mman.h>
7 #include <sys/time.h>
8 #include <inttypes.h>
9 #include <xenstore.h>
10 #include <xenctrl.h>
11 #include <xenguest.h>
12 #include <libxl.h>
13 #include <xenevtchn.h>
14 #include <xenforeignmemory.h>
15 #include <xen/io/xs_wire.h>
16 
17 #include "init-dom-json.h"
18 
19 #define XENSTORE_PFN_OFFSET 1
20 #define STR_MAX_LENGTH 128
21 
alloc_xs_page(struct xc_interface_core * xch,libxl_dominfo * info,uint64_t * xenstore_pfn)22 static int alloc_xs_page(struct xc_interface_core *xch,
23                          libxl_dominfo *info,
24                          uint64_t *xenstore_pfn)
25 {
26     int rc;
27     const xen_pfn_t base = GUEST_MAGIC_BASE >> XC_PAGE_SHIFT;
28     xen_pfn_t p2m = (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET;
29 
30     rc = xc_domain_setmaxmem(xch, info->domid,
31                              info->max_memkb + (XC_PAGE_SIZE/1024));
32     if (rc < 0)
33         return rc;
34 
35     rc = xc_domain_populate_physmap_exact(xch, info->domid, 1, 0, 0, &p2m);
36     if (rc < 0)
37         return rc;
38 
39     *xenstore_pfn = base + XENSTORE_PFN_OFFSET;
40     rc = xc_clear_domain_page(xch, info->domid, *xenstore_pfn);
41     if (rc < 0)
42         return rc;
43 
44     return 0;
45 }
46 
do_xs_write_dom(struct xs_handle * xsh,xs_transaction_t t,domid_t domid,char * path,char * val)47 static bool do_xs_write_dom(struct xs_handle *xsh, xs_transaction_t t,
48                             domid_t domid, char *path, char *val)
49 {
50     char full_path[STR_MAX_LENGTH];
51     struct xs_permissions perms[2];
52     int rc;
53 
54     perms[0].id = domid;
55     perms[0].perms = XS_PERM_NONE;
56     perms[1].id = 0;
57     perms[1].perms = XS_PERM_READ;
58 
59     rc = snprintf(full_path, STR_MAX_LENGTH,
60                   "/local/domain/%u/%s", domid, path);
61     if (rc < 0 || rc >= STR_MAX_LENGTH)
62         return false;
63     if (!xs_write(xsh, t, full_path, val, strlen(val)))
64         return false;
65     return xs_set_permissions(xsh, t, full_path, perms, 2);
66 }
67 
do_xs_write_libxl(struct xs_handle * xsh,xs_transaction_t t,domid_t domid,char * path,char * val)68 static bool do_xs_write_libxl(struct xs_handle *xsh, xs_transaction_t t,
69                               domid_t domid, char *path, char *val)
70 {
71     char full_path[STR_MAX_LENGTH];
72     int rc;
73 
74     rc = snprintf(full_path, STR_MAX_LENGTH,
75                   "/libxl/%u/%s", domid, path);
76     if (rc < 0 || rc >= STR_MAX_LENGTH)
77         return false;
78     return xs_write(xsh, t, full_path, val, strlen(val));
79 }
80 
do_xs_write_vm(struct xs_handle * xsh,xs_transaction_t t,libxl_uuid uuid,char * path,char * val)81 static bool do_xs_write_vm(struct xs_handle *xsh, xs_transaction_t t,
82                            libxl_uuid uuid, char *path, char *val)
83 {
84     char full_path[STR_MAX_LENGTH];
85     int rc;
86 
87     rc = snprintf(full_path, STR_MAX_LENGTH,
88                   "/vm/" LIBXL_UUID_FMT "/%s", LIBXL_UUID_BYTES(uuid), path);
89     if (rc < 0 || rc >= STR_MAX_LENGTH)
90         return false;
91     return xs_write(xsh, t, full_path, val, strlen(val));
92 }
93 
94 /*
95  * The xenstore nodes are the xenstore nodes libxl writes at domain
96  * creation.
97  *
98  * The list was retrieved by running xenstore-ls on a corresponding
99  * domain started by xl/libxl.
100  */
create_xenstore(struct xs_handle * xsh,libxl_dominfo * info,libxl_uuid uuid,evtchn_port_t xenstore_port)101 static int create_xenstore(struct xs_handle *xsh,
102                            libxl_dominfo *info, libxl_uuid uuid,
103                            evtchn_port_t xenstore_port)
104 {
105     domid_t domid;
106     unsigned int i;
107     char uuid_str[STR_MAX_LENGTH];
108     char dom_name_str[STR_MAX_LENGTH];
109     char vm_val_str[STR_MAX_LENGTH];
110     char id_str[STR_MAX_LENGTH];
111     char max_memkb_str[STR_MAX_LENGTH];
112     char target_memkb_str[STR_MAX_LENGTH];
113     char cpu_str[STR_MAX_LENGTH];
114     char xenstore_port_str[STR_MAX_LENGTH];
115     char ring_ref_str[STR_MAX_LENGTH];
116     xs_transaction_t t;
117     struct timeval start_time;
118     char start_time_str[STR_MAX_LENGTH];
119     int rc;
120 
121     if (gettimeofday(&start_time, NULL) < 0)
122         return -errno;
123     rc = snprintf(start_time_str, STR_MAX_LENGTH, "%jd.%02d",
124             (intmax_t)start_time.tv_sec, (int)start_time.tv_usec / 10000);
125     if (rc < 0 || rc >= STR_MAX_LENGTH)
126         return rc;
127 
128     domid = info->domid;
129     rc = snprintf(id_str, STR_MAX_LENGTH, "%u", domid);
130     if (rc < 0 || rc >= STR_MAX_LENGTH)
131         return rc;
132     rc = snprintf(dom_name_str, STR_MAX_LENGTH, "dom0less-%u", domid);
133     if (rc < 0 || rc >= STR_MAX_LENGTH)
134         return rc;
135     rc = snprintf(uuid_str, STR_MAX_LENGTH, LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
136     if (rc < 0 || rc >= STR_MAX_LENGTH)
137         return rc;
138     rc = snprintf(vm_val_str, STR_MAX_LENGTH,
139                   "vm/" LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid));
140     if (rc < 0 || rc >= STR_MAX_LENGTH)
141         return rc;
142     rc = snprintf(max_memkb_str, STR_MAX_LENGTH, "%"PRIu64, info->max_memkb);
143     if (rc < 0 || rc >= STR_MAX_LENGTH)
144         return rc;
145     rc = snprintf(target_memkb_str, STR_MAX_LENGTH, "%"PRIu64, info->current_memkb);
146     if (rc < 0 || rc >= STR_MAX_LENGTH)
147         return rc;
148     rc = snprintf(ring_ref_str, STR_MAX_LENGTH, "%lld",
149                   (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET);
150     if (rc < 0 || rc >= STR_MAX_LENGTH)
151         return rc;
152     rc = snprintf(xenstore_port_str, STR_MAX_LENGTH, "%u", xenstore_port);
153     if (rc < 0 || rc >= STR_MAX_LENGTH)
154         return rc;
155 
156 retry_transaction:
157     t = xs_transaction_start(xsh);
158     if (t == XBT_NULL)
159         return -errno;
160 
161     rc = -EIO;
162     /* /vm */
163     if (!do_xs_write_vm(xsh, t, uuid, "name", dom_name_str)) goto err;
164     if (!do_xs_write_vm(xsh, t, uuid, "uuid", uuid_str)) goto err;
165     if (!do_xs_write_vm(xsh, t, uuid, "start_time", start_time_str)) goto err;
166 
167     /* /domain */
168     if (!do_xs_write_dom(xsh, t, domid, "vm", vm_val_str)) goto err;
169     if (!do_xs_write_dom(xsh, t, domid, "name", dom_name_str)) goto err;
170     if (!do_xs_write_dom(xsh, t, domid, "cpu", "")) goto err;
171     for (i = 0; i < info->vcpu_max_id; i++) {
172         rc = snprintf(cpu_str, STR_MAX_LENGTH, "cpu/%u/availability/", i);
173         if (rc < 0 || rc >= STR_MAX_LENGTH)
174             goto err;
175         rc = -EIO;
176         if (!do_xs_write_dom(xsh, t, domid, cpu_str,
177                              (info->cpupool & (1 << i)) ? "online" : "offline"))
178             goto err;
179     }
180 
181     if (!do_xs_write_dom(xsh, t, domid, "memory", "")) goto err;
182     if (!do_xs_write_dom(xsh, t, domid, "memory/static-max", max_memkb_str)) goto err;
183     if (!do_xs_write_dom(xsh, t, domid, "memory/target", target_memkb_str)) goto err;
184     if (!do_xs_write_dom(xsh, t, domid, "memory/videoram", "-1")) goto err;
185 
186     if (!do_xs_write_dom(xsh, t, domid, "device", "")) goto err;
187     if (!do_xs_write_dom(xsh, t, domid, "device/suspend", "")) goto err;
188     if (!do_xs_write_dom(xsh, t, domid, "device/suspend/event-channel", "")) goto err;
189 
190     if (!do_xs_write_dom(xsh, t, domid, "control", "")) goto err;
191     if (!do_xs_write_dom(xsh, t, domid, "control/shutdown", "")) goto err;
192     if (!do_xs_write_dom(xsh, t, domid, "control/feature-poweroff", "1")) goto err;
193     if (!do_xs_write_dom(xsh, t, domid, "control/feature-reboot", "1")) goto err;
194     if (!do_xs_write_dom(xsh, t, domid, "control/feature-suspend", "")) goto err;
195     if (!do_xs_write_dom(xsh, t, domid, "control/sysrq", "")) goto err;
196     if (!do_xs_write_dom(xsh, t, domid, "control/platform-feature-multiprocessor-suspend", "1")) goto err;
197     if (!do_xs_write_dom(xsh, t, domid, "control/platform-feature-xs_reset_watches", "1")) goto err;
198 
199     if (!do_xs_write_dom(xsh, t, domid, "domid", id_str)) goto err;
200     if (!do_xs_write_dom(xsh, t, domid, "data", "")) goto err;
201     if (!do_xs_write_dom(xsh, t, domid, "drivers", "")) goto err;
202     if (!do_xs_write_dom(xsh, t, domid, "feature", "")) goto err;
203     if (!do_xs_write_dom(xsh, t, domid, "attr", "")) goto err;
204 
205     if (!do_xs_write_dom(xsh, t, domid, "store/port", xenstore_port_str)) goto err;
206     if (!do_xs_write_dom(xsh, t, domid, "store/ring-ref", ring_ref_str)) goto err;
207 
208     if (!do_xs_write_libxl(xsh, t, domid, "type", "pvh")) goto err;
209     if (!do_xs_write_libxl(xsh, t, domid, "dm-version", "qemu_xen")) goto err;
210 
211     if (!xs_transaction_end(xsh, t, false)) {
212         if (errno == EAGAIN)
213             goto retry_transaction;
214         else
215             return -errno;
216     }
217 
218     return 0;
219 
220 err:
221     xs_transaction_end(xsh, t, true);
222     return rc;
223 }
224 
init_domain(struct xs_handle * xsh,struct xc_interface_core * xch,xenforeignmemory_handle * xfh,libxl_dominfo * info)225 static int init_domain(struct xs_handle *xsh,
226                        struct xc_interface_core *xch,
227                        xenforeignmemory_handle *xfh,
228                        libxl_dominfo *info)
229 {
230     libxl_uuid uuid;
231     uint64_t xenstore_evtchn, xenstore_pfn;
232     int rc;
233     struct xenstore_domain_interface *intf;
234 
235     printf("Init dom0less domain: %u\n", info->domid);
236 
237     rc = xc_hvm_param_get(xch, info->domid, HVM_PARAM_STORE_EVTCHN,
238                           &xenstore_evtchn);
239     if (rc != 0) {
240         printf("Failed to get HVM_PARAM_STORE_EVTCHN\n");
241         return 1;
242     }
243 
244     /* no xen,enhanced; nothing to do */
245     if (!xenstore_evtchn)
246         return 0;
247 
248     /* Alloc xenstore page */
249     if (alloc_xs_page(xch, info, &xenstore_pfn) != 0) {
250         printf("Error on alloc magic pages\n");
251         return 1;
252     }
253 
254     intf = xenforeignmemory_map(xfh, info->domid, PROT_READ | PROT_WRITE, 1,
255                                 &xenstore_pfn, NULL);
256     if (!intf) {
257         printf("Error mapping xenstore page\n");
258         return 1;
259     }
260     intf->connection = XENSTORE_RECONNECT;
261     xenforeignmemory_unmap(xfh, intf, 1);
262 
263     rc = xc_dom_gnttab_seed(xch, info->domid, true,
264                             (xen_pfn_t)-1, xenstore_pfn, 0, 0);
265     if (rc)
266         err(1, "xc_dom_gnttab_seed");
267 
268     libxl_uuid_generate(&uuid);
269     xc_domain_sethandle(xch, info->domid, libxl_uuid_bytearray(&uuid));
270 
271     rc = gen_stub_json_config(info->domid, &uuid);
272     if (rc)
273         err(1, "gen_stub_json_config");
274 
275     /* Now everything is ready: set HVM_PARAM_STORE_PFN */
276     rc = xc_hvm_param_set(xch, info->domid, HVM_PARAM_STORE_PFN,
277                           xenstore_pfn);
278     if (rc < 0)
279         return rc;
280 
281     rc = create_xenstore(xsh, info, uuid, xenstore_evtchn);
282     if (rc)
283         err(1, "writing to xenstore");
284 
285     rc = xs_introduce_domain(xsh, info->domid,
286             (GUEST_MAGIC_BASE >> XC_PAGE_SHIFT) + XENSTORE_PFN_OFFSET,
287             xenstore_evtchn);
288     if (!rc)
289         err(1, "xs_introduce_domain");
290     return 0;
291 }
292 
293 /* Check if domain has been configured in XS */
domain_exists(struct xs_handle * xsh,int domid)294 static bool domain_exists(struct xs_handle *xsh, int domid)
295 {
296     return xs_is_domain_introduced(xsh, domid);
297 }
298 
main(int argc,char ** argv)299 int main(int argc, char **argv)
300 {
301     libxl_dominfo *info = NULL;
302     libxl_ctx *ctx;
303     int nb_vm = 0, rc = 0, i;
304     struct xs_handle *xsh = NULL;
305     struct xc_interface_core *xch = NULL;
306     xenforeignmemory_handle *xfh = NULL;
307 
308     /* TODO reuse libxl xsh connection */
309     xsh = xs_open(0);
310     xch = xc_interface_open(0, 0, 0);
311     xfh = xenforeignmemory_open(0, 0);
312     if (xsh == NULL || xch == NULL || xfh == NULL) {
313         fprintf(stderr, "Cannot open xc/xs/xenforeignmemory interfaces");
314         rc = -errno;
315         goto out;
316     }
317 
318     rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, NULL);
319     if (rc) {
320         fprintf(stderr, "cannot init xl context\n");
321         goto out;
322     }
323 
324     info = libxl_list_domain(ctx, &nb_vm);
325     if (!info) {
326         fprintf(stderr, "libxl_list_vm failed.\n");
327         rc = -1;
328         goto out;
329     }
330 
331     for (i = 0; i < nb_vm; i++) {
332         domid_t domid = info[i].domid;
333 
334         /* Don't need to check for Dom0 */
335         if (!domid)
336             continue;
337 
338         printf("Checking domid: %u\n", domid);
339         if (!domain_exists(xsh, domid)) {
340             rc = init_domain(xsh, xch, xfh, &info[i]);
341             if (rc < 0) {
342                 fprintf(stderr, "init_domain failed.\n");
343                 goto out;
344             }
345         } else {
346             printf("Domain %u has already been initialized\n", domid);
347         }
348     }
349 out:
350     libxl_dominfo_list_free(info, nb_vm);
351     return rc;
352 }
353