Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Further cleanup
[palacios.git] / palacios / src / palacios / vmm_dev_mgr.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm_dev_mgr.h>
21 #include <palacios/vm_guest.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_decoder.h>
24
25 #ifdef V3_CONFIG_CHECKPOINT
26 #include <palacios/vmm_checkpoint.h>
27 #endif
28
29
30 #ifndef V3_CONFIG_DEBUG_DEV_MGR
31 #undef PrintDebug
32 #define PrintDebug(fmt, args...)
33 #endif
34
35
36 static struct hashtable * master_dev_table = NULL;
37
38 static uint_t dev_hash_fn(addr_t key) {
39     char * name = (char *)key;
40     return v3_hash_buffer((uchar_t *)name, strlen(name));
41 }
42
43 static int dev_eq_fn(addr_t key1, addr_t key2) {
44     char * name1 = (char *)key1;
45     char * name2 = (char *)key2;
46     
47     return (strcmp(name1, name2) == 0);
48 }
49
50
51 int V3_init_devices() {
52     extern struct v3_device_info __start__v3_devices[];
53     extern struct v3_device_info __stop__v3_devices[];
54     struct v3_device_info * tmp_dev =  __start__v3_devices;
55     int i = 0;
56
57 #ifdef V3_CONFIG_DEBUG_DEV_MGR
58     {
59         int num_devices = (__stop__v3_devices - __start__v3_devices) / sizeof(struct v3_device_info);
60         PrintDebug("%d Virtual devices registered with Palacios\n", num_devices);
61     }
62 #endif
63
64     PrintDebug("Start addres=%p, Stop address=%p\n", __start__v3_devices, __stop__v3_devices);
65
66     master_dev_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
67
68
69
70     while (tmp_dev != __stop__v3_devices) {
71         V3_Print("Registering Device: %s\n", tmp_dev->name);
72
73         if (v3_htable_search(master_dev_table, (addr_t)(tmp_dev->name))) {
74             PrintError("Multiple instance of device (%s)\n", tmp_dev->name);
75             return -1;
76         }
77
78         if (v3_htable_insert(master_dev_table, 
79                              (addr_t)(tmp_dev->name), 
80                              (addr_t)(tmp_dev->init)) == 0) {
81             PrintError("Could not add device %s to master list\n", tmp_dev->name);
82             return -1;
83         }
84
85         tmp_dev = &(__start__v3_devices[++i]);
86     }
87
88
89     return 0;
90 }
91
92
93 int V3_deinit_devices() {    
94     v3_free_htable(master_dev_table, 0, 0);
95     return 0;
96 }
97
98
99 int v3_init_dev_mgr(struct v3_vm_info * vm) {
100     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
101
102     INIT_LIST_HEAD(&(mgr->dev_list));
103     mgr->num_devs = 0;
104
105     mgr->dev_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
106
107     INIT_LIST_HEAD(&(mgr->blk_list));
108     INIT_LIST_HEAD(&(mgr->net_list));
109     INIT_LIST_HEAD(&(mgr->char_list));
110     INIT_LIST_HEAD(&(mgr->cons_list));
111
112     mgr->blk_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
113     mgr->net_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
114     mgr->char_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
115     mgr->cons_table = v3_create_htable(0, dev_hash_fn, dev_eq_fn);
116     
117     return 0;
118 }
119
120
121 int v3_free_vm_devices(struct v3_vm_info * vm) {
122     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
123     struct vm_device * dev;
124     struct vm_device * tmp;
125
126     list_for_each_entry_safe(dev, tmp, &(mgr->dev_list), dev_link) {
127         v3_remove_device(dev);
128     }
129
130     return 0;
131 }
132
133 #ifdef V3_CONFIG_CHECKPOINT
134
135 int v3_save_vm_devices(struct v3_vm_info * vm, struct v3_chkpt * chkpt) {
136     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
137     struct vm_device * dev;
138     struct v3_chkpt_ctx * dev_mgr_ctx = NULL;
139
140     uint32_t num_saved_devs = 0;
141     uint32_t table_len = mgr->num_devs * 32;
142     char * name_table = NULL;
143     uint32_t tbl_offset = 0;
144     
145     name_table = V3_Malloc(table_len);
146
147     memset(name_table, 0, table_len);
148     
149
150     dev_mgr_ctx = v3_chkpt_open_ctx(chkpt, NULL, "devices");
151
152     list_for_each_entry(dev, &(mgr->dev_list), dev_link) {
153         if (dev->ops->save) {
154             strncpy(name_table + tbl_offset, dev->name, 32);
155             tbl_offset += 32;
156             num_saved_devs++;
157         }         
158     }
159
160     v3_chkpt_save(dev_mgr_ctx, "num_devs", 4, &num_saved_devs); 
161     v3_chkpt_save(dev_mgr_ctx, "names", num_saved_devs*32, name_table);
162     
163     v3_chkpt_close_ctx(dev_mgr_ctx);
164
165     V3_Free(name_table);
166
167     list_for_each_entry(dev, &(mgr->dev_list), dev_link) {
168         if (dev->ops->save) {
169             struct v3_chkpt_ctx * dev_ctx = NULL;
170             
171             V3_Print("Saving state for device (%s)\n", dev->name);
172             
173             dev_ctx = v3_chkpt_open_ctx(chkpt, NULL, dev->name);
174
175             dev->ops->save(dev_ctx, dev->private_data);
176
177             v3_chkpt_close_ctx(dev_ctx);
178
179             // Error checking?? 
180         } else {
181             PrintError("Error: %s save() not implemented\n",  dev->name);
182         }
183     }
184
185     return 0;
186 }
187
188
189 int v3_load_vm_devices(struct v3_vm_info * vm, struct v3_chkpt * chkpt) {
190     struct vm_device * dev;
191     struct v3_chkpt_ctx * dev_mgr_ctx = NULL;
192     uint32_t num_devs = 0;
193     char * name_table = NULL;
194     int i = 0;
195
196     dev_mgr_ctx = v3_chkpt_open_ctx(chkpt, NULL, "devices");
197
198     v3_chkpt_load(dev_mgr_ctx, "num_devs", 4, &num_devs);
199
200     V3_Print("Loading State for %d devices\n", num_devs);
201     
202     name_table = V3_Malloc(32 * num_devs);
203
204     v3_chkpt_load(dev_mgr_ctx, "names", 32 * num_devs, name_table);
205
206     v3_chkpt_close_ctx(dev_mgr_ctx);
207
208     for (i = 0; i < num_devs; i++) {
209         char * name = &(name_table[i * 32]);
210         struct v3_chkpt_ctx * dev_ctx = NULL;
211         dev = v3_find_dev(vm, name);
212
213         if (!dev) {
214             PrintError("Tried to load state into non existant device: %s\n", name);
215             continue;
216         }
217
218         if (!dev->ops->load) {
219             PrintError("Error Device (%s) does not support load operation\n", name);
220             continue;
221         }
222
223         dev_ctx = v3_chkpt_open_ctx(chkpt, NULL, name);
224
225         if (!dev_ctx) {
226             PrintError("Error missing device context (%s)\n", name);
227             continue;
228         }
229
230
231         dev->ops->load(dev_ctx, dev->private_data);
232
233         v3_chkpt_close_ctx(dev_ctx);
234     }
235
236     V3_Free(name_table);
237
238     return 0;
239 }
240
241
242 #endif
243
244 static int free_frontends(struct v3_vm_info * vm, struct vmm_dev_mgr * mgr);
245
246 int v3_deinit_dev_mgr(struct v3_vm_info * vm) {
247     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
248     
249     // clear frontend lists
250
251     free_frontends(vm, mgr);
252
253
254
255     v3_free_htable(mgr->dev_table, 0, 0);
256
257     return 0;
258 }
259
260
261
262 int v3_create_device(struct v3_vm_info * vm, const char * dev_name, v3_cfg_tree_t * cfg) {
263     int (*dev_init)(struct v3_vm_info * vm, void * cfg_data);
264
265     dev_init = (void *)v3_htable_search(master_dev_table, (addr_t)dev_name);
266
267     if (dev_init == NULL) {
268         PrintError("Could not find device %s in master device table\n", dev_name);
269         return -1;
270     }
271
272
273     if (dev_init(vm, cfg) == -1) {
274         PrintError("Could not initialize Device %s\n", dev_name);
275         return -1;
276     }
277
278     return 0;
279 }
280
281
282
283
284 struct vm_device * v3_find_dev(struct v3_vm_info * vm, const char * dev_name) {
285     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
286
287     if (!dev_name) {
288         return NULL;
289     }
290
291     return (struct vm_device *)v3_htable_search(mgr->dev_table, (addr_t)dev_name);
292 }
293
294
295 /****************************************************************/
296 /* The remaining functions are called by the devices themselves */
297 /****************************************************************/
298
299 typedef enum {DEV_IO_HOOK, DEV_MSR_HOOK, DEV_CPUID_HOOK, DEV_MEM_HOOK, DEV_HCALL_HOOK} dev_rsrc_type_t;
300
301 struct dev_rsrc {
302     dev_rsrc_type_t type;
303     uint64_t rsrc;
304
305     struct list_head node;
306 };
307
308
309
310 static int add_resource(struct vm_device * dev, dev_rsrc_type_t type, uint64_t rsrc_id) {
311     struct dev_rsrc * resource = NULL;
312
313     resource = V3_Malloc(sizeof(struct dev_rsrc));
314
315     if (resource == NULL) {
316         PrintError("Error: Could not allocate device resource\n");
317         return -1;
318     }
319
320     resource->rsrc = rsrc_id;
321     resource->type = type;
322
323     list_add(&(resource->node), &(dev->res_hooks));
324     return 0;
325 }
326
327 static int free_resource(struct vm_device * dev, dev_rsrc_type_t type, uint64_t rsrc_id) {
328     struct dev_rsrc * resource = NULL;
329     struct dev_rsrc * tmp;
330
331     list_for_each_entry_safe(resource, tmp, &(dev->res_hooks), node) {
332         if ((resource->type == type) && 
333             (resource->rsrc == rsrc_id)) {
334
335             list_del(&(resource->node));
336             V3_Free(resource);
337             
338             return 0;
339         }
340     }
341
342     return -1;
343 }
344
345
346 int v3_dev_hook_io(struct vm_device * dev, uint16_t port,
347                    int (*read)(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data),
348                    int (*write)(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data)) {
349     int ret = 0;
350     
351     ret = v3_hook_io_port(dev->vm, port, 
352                           (int (*)(struct guest_info * core, uint16_t, void *, uint_t, void *))read, 
353                           (int (*)(struct guest_info * core, uint16_t, void *, uint_t, void *))write, 
354                           (void *)dev->private_data);
355
356     if (ret == -1) {
357         return -1;
358     }
359
360     if (add_resource(dev, DEV_IO_HOOK, port) == -1) {
361         v3_unhook_io_port(dev->vm, port);
362         PrintError("Could not allocate io hook dev state\n");
363         return -1;
364     }
365     
366     return 0;
367 }
368
369
370 int v3_dev_unhook_io(struct vm_device * dev, uint16_t port) {
371     if (free_resource(dev, DEV_IO_HOOK, port) == 0) {
372         return v3_unhook_io_port(dev->vm, port);           
373     } 
374
375     return -1;
376 }
377
378
379 int v3_dev_hook_msr(struct vm_device * dev, uint32_t msr,
380                     int (*read)(struct guest_info * core, uint32_t msr, struct v3_msr * dst, void * priv_data),
381                     int (*write)(struct guest_info * core, uint32_t msr, struct v3_msr src, void * priv_data)) {
382     int ret = 0;
383
384     ret = v3_hook_msr(dev->vm, msr, read, write, dev->private_data);
385
386     if (ret == -1) {
387         return -1;
388     }
389
390     if (add_resource(dev, DEV_MSR_HOOK, msr) == -1) {
391         v3_unhook_msr(dev->vm, msr);
392         return -1;
393     }
394
395     return 0;
396 }
397                   
398 int v3_dev_unhook_msr(struct vm_device * dev, uint32_t msr) {
399     if (free_resource(dev, DEV_MSR_HOOK, msr) == 0) {
400         return v3_unhook_msr(dev->vm, msr);
401     }
402
403     return -1;
404 }
405
406
407
408
409 int v3_remove_device(struct vm_device * dev) {
410     struct vmm_dev_mgr * mgr = &(dev->vm->dev_mgr);
411     struct dev_rsrc * resource = NULL;
412     struct dev_rsrc * tmp;
413
414     list_for_each_entry_safe(resource, tmp, &(dev->res_hooks), node) {
415         if (resource->type == DEV_IO_HOOK) {
416             v3_unhook_io_port(dev->vm, (uint16_t)(resource->rsrc));
417         } else if (resource->type == DEV_MSR_HOOK) {
418             v3_unhook_msr(dev->vm, (uint32_t)(resource->rsrc));
419         }
420
421         list_del(&(resource->node));
422         V3_Free(resource);    
423     }
424
425     if (dev->ops->free) {
426         dev->ops->free(dev->private_data);
427     } else {
428         PrintError("Error: %s free() not implemented\n",  dev->name);
429     }
430
431     list_del(&(dev->dev_link));
432     mgr->num_devs--;
433
434     dev->vm = NULL;
435
436     V3_Free(dev);
437     return -1;
438 }
439
440
441 struct vm_device * v3_add_device(struct v3_vm_info * vm,
442                                  char * name, 
443                                  struct v3_device_ops * ops, 
444                                  void * private_data) {
445     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
446     struct vm_device * dev = NULL;
447
448
449     // Check if we already registered a device of the same name
450     if (v3_htable_search(mgr->dev_table, (addr_t)name) != (addr_t)NULL) {
451         PrintError("Device with name (%s) already registered with VM\n", name); 
452         return NULL;
453     }
454
455     dev = (struct vm_device *)V3_Malloc(sizeof(struct vm_device));
456
457     if (dev == NULL) {
458         return NULL;
459     }
460
461     INIT_LIST_HEAD(&(dev->res_hooks));
462
463     strncpy(dev->name, name, 32);
464     dev->ops = ops;
465     dev->private_data = private_data;
466
467     dev->vm = vm;
468
469     list_add(&(dev->dev_link), &(mgr->dev_list));
470     mgr->num_devs++;
471
472     v3_htable_insert(mgr->dev_table, (addr_t)(dev->name), (addr_t)dev);
473
474     return dev;
475 }
476
477
478 void v3_print_dev_mgr(struct v3_vm_info * vm) {
479     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
480     struct vm_device * dev;
481
482     V3_Print("%d devices registered with manager\n", mgr->num_devs);
483
484     list_for_each_entry(dev, &(mgr->dev_list), dev_link) {
485         V3_Print("Device: %s\n", dev->name);
486     }
487
488     return;
489 }
490
491
492
493
494 struct blk_frontend {
495     int (*connect)(struct v3_vm_info * vm, 
496                     void * frontend_data, 
497                     struct v3_dev_blk_ops * ops, 
498                     v3_cfg_tree_t * cfg, 
499                     void * priv_data);
500         
501
502     struct list_head blk_node;
503
504     void * priv_data;
505 };
506
507
508
509 int v3_dev_add_blk_frontend(struct v3_vm_info * vm, 
510                             char * name, 
511                             int (*connect)(struct v3_vm_info * vm, 
512                                             void * frontend_data, 
513                                             struct v3_dev_blk_ops * ops, 
514                                             v3_cfg_tree_t * cfg, 
515                                             void * priv_data), 
516                             void * priv_data) {
517
518     struct blk_frontend * frontend = NULL;
519
520     frontend = (struct blk_frontend *)V3_Malloc(sizeof(struct blk_frontend));
521     memset(frontend, 0, sizeof(struct blk_frontend));
522     
523     frontend->connect = connect;
524     frontend->priv_data = priv_data;
525         
526     list_add(&(frontend->blk_node), &(vm->dev_mgr.blk_list));
527     v3_htable_insert(vm->dev_mgr.blk_table, (addr_t)(name), (addr_t)frontend);
528
529     return 0;
530 }
531
532 int v3_dev_connect_blk(struct v3_vm_info * vm, 
533                        char * frontend_name, 
534                        struct v3_dev_blk_ops * ops, 
535                        v3_cfg_tree_t * cfg, 
536                        void * private_data) {
537
538     struct blk_frontend * frontend = NULL;
539
540     frontend = (struct blk_frontend *)v3_htable_search(vm->dev_mgr.blk_table,
541                                                        (addr_t)frontend_name);
542     
543     if (frontend == NULL) {
544         PrintError("Could not find frontend blk device %s\n", frontend_name);
545         return 0;
546     }
547
548     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
549         PrintError("Error connecting to block frontend %s\n", frontend_name);
550         return -1;
551     }
552
553     return 0;
554 }
555
556
557
558 struct net_frontend {
559     int (*connect)(struct v3_vm_info * vm, 
560                     void * frontend_data, 
561                     struct v3_dev_net_ops * ops, 
562                     v3_cfg_tree_t * cfg, 
563                     void * priv_data);
564     
565
566     struct list_head net_node;
567
568     void * priv_data;
569 };
570
571
572 int v3_dev_add_net_frontend(struct v3_vm_info * vm, 
573                             char * name, 
574                             int (*connect)(struct v3_vm_info * vm, 
575                                             void * frontend_data, 
576                                             struct v3_dev_net_ops * ops, 
577                                             v3_cfg_tree_t * cfg, 
578                                             void * private_data), 
579                             void * priv_data)
580 {
581     struct net_frontend * frontend = NULL;
582
583     frontend = (struct net_frontend *)V3_Malloc(sizeof(struct net_frontend));
584     memset(frontend, 0, sizeof(struct net_frontend));
585     
586     frontend->connect = connect;
587     frontend->priv_data = priv_data;
588         
589     list_add(&(frontend->net_node), &(vm->dev_mgr.net_list));
590     v3_htable_insert(vm->dev_mgr.net_table, (addr_t)(name), (addr_t)frontend);
591
592     return 0;
593 }
594
595
596 int v3_dev_connect_net(struct v3_vm_info * vm, 
597                        char * frontend_name, 
598                        struct v3_dev_net_ops * ops, 
599                        v3_cfg_tree_t * cfg, 
600                        void * private_data)
601 {
602     struct net_frontend * frontend = NULL;
603
604     frontend = (struct net_frontend *)v3_htable_search(vm->dev_mgr.net_table,
605                                                        (addr_t)frontend_name);
606     
607     if (frontend == NULL) {
608         PrintError("Could not find frontend net device %s\n", frontend_name);
609         return 0;
610     }
611
612     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
613         PrintError("Error connecting to net frontend %s\n", frontend_name);
614         return -1;
615     }
616
617     return 0;
618 }
619
620
621 struct cons_frontend {
622     int (*connect)(struct v3_vm_info * vm, 
623                    void * frontend_data, 
624                    struct v3_dev_console_ops * ops, 
625                    v3_cfg_tree_t * cfg, 
626                    void * priv_data);
627     
628
629     struct list_head cons_node;
630
631     void * priv_data;
632 };
633
634 int v3_dev_add_console_frontend(struct v3_vm_info * vm, 
635                                 char * name, 
636                                 int (*connect)(struct v3_vm_info * vm, 
637                                                void * frontend_data, 
638                                                struct v3_dev_console_ops * ops, 
639                                                v3_cfg_tree_t * cfg, 
640                                                void * private_data), 
641                                 void * priv_data)
642 {
643     struct cons_frontend * frontend = NULL;
644
645     frontend = (struct cons_frontend *)V3_Malloc(sizeof(struct cons_frontend));
646     memset(frontend, 0, sizeof(struct cons_frontend));
647     
648     frontend->connect = connect;
649     frontend->priv_data = priv_data;
650         
651     list_add(&(frontend->cons_node), &(vm->dev_mgr.cons_list));
652     v3_htable_insert(vm->dev_mgr.cons_table, (addr_t)(name), (addr_t)frontend);
653
654     return 0;
655 }
656
657
658 int v3_dev_connect_console(struct v3_vm_info * vm, 
659                            char * frontend_name, 
660                            struct v3_dev_console_ops * ops, 
661                            v3_cfg_tree_t * cfg, 
662                            void * private_data)
663 {
664     struct cons_frontend * frontend = NULL;
665
666     frontend = (struct cons_frontend *)v3_htable_search(vm->dev_mgr.cons_table,
667                                                         (addr_t)frontend_name);
668     
669     if (frontend == NULL) {
670         PrintError("Could not find frontend console device %s\n", frontend_name);
671         return 0;
672     }
673     
674     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
675         PrintError("Error connecting to console frontend %s\n", frontend_name);
676         return -1;
677     }
678
679     return 0;
680 }
681
682 struct char_frontend {
683     int (*connect)(struct v3_vm_info * vm, 
684                    void * frontend_data, 
685                    struct v3_dev_char_ops * ops, 
686                    v3_cfg_tree_t * cfg, 
687                    void * priv_data, 
688                    void ** push_fn_arg);
689     
690
691     struct list_head char_node;
692
693     void * priv_data;
694 };
695
696 int v3_dev_add_char_frontend(struct v3_vm_info * vm, 
697                              char * name, 
698                              int (*connect)(struct v3_vm_info * vm, 
699                                             void * frontend_data, 
700                                             struct v3_dev_char_ops * ops, 
701                                             v3_cfg_tree_t * cfg, 
702                                             void * private_data, 
703                                             void ** push_fn_arg), 
704                              void * priv_data)
705 {
706     struct char_frontend * frontend = NULL;
707
708     frontend = (struct char_frontend *)V3_Malloc(sizeof(struct char_frontend));
709     memset(frontend, 0, sizeof(struct char_frontend));
710     
711     frontend->connect = connect;
712     frontend->priv_data = priv_data;
713         
714     list_add(&(frontend->char_node), &(vm->dev_mgr.char_list));
715     v3_htable_insert(vm->dev_mgr.char_table, (addr_t)(name), (addr_t)frontend);
716
717     return 0;
718 }
719
720
721 int v3_dev_connect_char(struct v3_vm_info * vm, 
722                         char * frontend_name, 
723                         struct v3_dev_char_ops * ops, 
724                         v3_cfg_tree_t * cfg, 
725                         void * private_data, 
726                         void ** push_fn_arg)
727 {
728     struct char_frontend * frontend = NULL;
729
730     frontend = (struct char_frontend *)v3_htable_search(vm->dev_mgr.char_table,
731                                                         (addr_t)frontend_name);
732     
733     if (frontend == NULL) {
734         PrintError("Could not find frontend char device %s\n", frontend_name);
735         return 0;
736     }
737     
738     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data, push_fn_arg) == -1) {
739         PrintError("Error connecting to char frontend %s\n", frontend_name);
740         return -1;
741     }
742
743     return 0;
744 }
745
746
747
748 static int free_frontends(struct v3_vm_info * vm, struct vmm_dev_mgr * mgr) {
749     struct char_frontend * chr = NULL;
750     struct char_frontend * tmp_chr = NULL;
751     struct cons_frontend * cons = NULL;
752     struct cons_frontend * tmp_cons = NULL;
753     struct net_frontend * net = NULL;
754     struct net_frontend * tmp_net = NULL;
755     struct blk_frontend * blk = NULL;
756     struct blk_frontend * tmp_blk = NULL;
757
758
759
760     list_for_each_entry_safe(chr, tmp_chr, &(mgr->char_list), char_node) {
761         list_del(&(chr->char_node));
762         V3_Free(chr);
763     }
764
765     list_for_each_entry_safe(cons, tmp_cons, &(mgr->cons_list), cons_node) {
766         list_del(&(cons->cons_node));
767         V3_Free(cons);
768     }
769
770     list_for_each_entry_safe(net, tmp_net, &(mgr->net_list), net_node) {
771         list_del(&(net->net_node));
772         V3_Free(net);
773     }
774
775     list_for_each_entry_safe(blk, tmp_blk, &(mgr->blk_list), blk_node) {
776         list_del(&(blk->blk_node));
777         V3_Free(blk);
778     }
779
780     v3_free_htable(mgr->blk_table, 0, 0);
781     v3_free_htable(mgr->net_table, 0, 0);
782     v3_free_htable(mgr->char_table, 0, 0);
783     v3_free_htable(mgr->cons_table, 0, 0);
784
785
786     return 0;
787 }