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.


added error check for configurations with replicated device IDs
[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         PrintDebug("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
154         if (dev->ops->save) {
155             struct v3_chkpt_ctx * dev_ctx = NULL;
156             
157             V3_Print("Saving state for device (%s)\n", dev->name);
158             
159             dev_ctx = v3_chkpt_open_ctx(chkpt, dev_mgr_ctx, dev->name);
160
161             dev->ops->save(dev_ctx, dev->private_data);
162
163             v3_chkpt_close_ctx(dev_ctx);
164
165             // Error checking?? 
166
167             strncpy(name_table + tbl_offset, dev->name, 32);
168             tbl_offset += 32;
169             num_saved_devs++;
170         } else {
171             PrintError("Error: %s save() not implemented\n",  dev->name);
172         }
173     }
174
175     
176     // Specify which devices were saved
177     v3_chkpt_save(dev_mgr_ctx, "num_devs", 4, &num_saved_devs); 
178     v3_chkpt_save(dev_mgr_ctx, "names", table_len, name_table);
179     V3_Free(name_table);
180
181     v3_chkpt_close_ctx(dev_mgr_ctx);
182     
183     return 0;
184 }
185
186
187 int v3_load_vm_devices(struct v3_vm_info * vm, struct v3_chkpt * chkpt) {
188     struct vm_device * dev;
189     struct v3_chkpt_ctx * dev_mgr_ctx = NULL;
190     uint32_t num_devs = 0;
191     char * name_table = NULL;
192     int i = 0;
193
194     dev_mgr_ctx = v3_chkpt_open_ctx(chkpt, NULL, "devices");
195
196     v3_chkpt_load(dev_mgr_ctx, "num_devs", 4, &num_devs);
197
198     V3_Print("Loading State for %d devices\n", num_devs);
199     
200     name_table = V3_Malloc(32 * num_devs);
201
202     v3_chkpt_load(dev_mgr_ctx, "names", 32 * num_devs, name_table);
203
204     for (i = 0; i < num_devs; i++) {
205         char * name = &(name_table[i * 32]);
206         struct v3_chkpt_ctx * dev_ctx = NULL;
207         dev = v3_find_dev(vm, name);
208
209         if (!dev) {
210             PrintError("Tried to load state into non existant device: %s\n", name);
211             continue;
212         }
213
214         if (!dev->ops->load) {
215             PrintError("Error Device (%s) does not support load operation\n", name);
216             continue;
217         }
218
219         dev_ctx = v3_chkpt_open_ctx(chkpt, dev_mgr_ctx, name);
220
221         if (!dev_ctx) {
222             PrintError("Error missing device context (%s)\n", name);
223             continue;
224         }
225
226
227         dev->ops->load(dev_ctx, dev->private_data);
228     }
229
230     return 0;
231 }
232
233
234 #endif
235
236 static int free_frontends(struct v3_vm_info * vm, struct vmm_dev_mgr * mgr);
237
238 int v3_deinit_dev_mgr(struct v3_vm_info * vm) {
239     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
240     
241     // clear frontend lists
242
243     free_frontends(vm, mgr);
244
245
246
247     v3_free_htable(mgr->dev_table, 0, 0);
248
249     return 0;
250 }
251
252
253
254 int v3_create_device(struct v3_vm_info * vm, const char * dev_name, v3_cfg_tree_t * cfg) {
255     int (*dev_init)(struct v3_vm_info * vm, void * cfg_data);
256
257     dev_init = (void *)v3_htable_search(master_dev_table, (addr_t)dev_name);
258
259     if (dev_init == NULL) {
260         PrintError("Could not find device %s in master device table\n", dev_name);
261         return -1;
262     }
263
264
265     if (dev_init(vm, cfg) == -1) {
266         PrintError("Could not initialize Device %s\n", dev_name);
267         return -1;
268     }
269
270     return 0;
271 }
272
273
274
275
276 struct vm_device * v3_find_dev(struct v3_vm_info * vm, const char * dev_name) {
277     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
278
279     if (!dev_name) {
280         return NULL;
281     }
282
283     return (struct vm_device *)v3_htable_search(mgr->dev_table, (addr_t)dev_name);
284 }
285
286
287 /****************************************************************/
288 /* The remaining functions are called by the devices themselves */
289 /****************************************************************/
290
291 typedef enum {DEV_IO_HOOK, DEV_MSR_HOOK, DEV_CPUID_HOOK, DEV_MEM_HOOK, DEV_HCALL_HOOK} dev_rsrc_type_t;
292
293 struct dev_rsrc {
294     dev_rsrc_type_t type;
295     uint64_t rsrc;
296
297     struct list_head node;
298 };
299
300
301
302 static int add_resource(struct vm_device * dev, dev_rsrc_type_t type, uint64_t rsrc_id) {
303     struct dev_rsrc * resource = NULL;
304
305     resource = V3_Malloc(sizeof(struct dev_rsrc));
306
307     if (resource == NULL) {
308         PrintError("Error: Could not allocate device resource\n");
309         return -1;
310     }
311
312     resource->rsrc = rsrc_id;
313     resource->type = type;
314
315     list_add(&(resource->node), &(dev->res_hooks));
316     return 0;
317 }
318
319 static int free_resource(struct vm_device * dev, dev_rsrc_type_t type, uint64_t rsrc_id) {
320     struct dev_rsrc * resource = NULL;
321     struct dev_rsrc * tmp;
322
323     list_for_each_entry_safe(resource, tmp, &(dev->res_hooks), node) {
324         if ((resource->type == type) && 
325             (resource->rsrc == rsrc_id)) {
326
327             list_del(&(resource->node));
328             V3_Free(resource);
329             
330             return 0;
331         }
332     }
333
334     return -1;
335 }
336
337
338 int v3_dev_hook_io(struct vm_device * dev, uint16_t port,
339                    int (*read)(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data),
340                    int (*write)(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data)) {
341     int ret = 0;
342     
343     ret = v3_hook_io_port(dev->vm, port, 
344                           (int (*)(struct guest_info * core, uint16_t, void *, uint_t, void *))read, 
345                           (int (*)(struct guest_info * core, uint16_t, void *, uint_t, void *))write, 
346                           (void *)dev->private_data);
347
348     if (ret == -1) {
349         return -1;
350     }
351
352     if (add_resource(dev, DEV_IO_HOOK, port) == -1) {
353         v3_unhook_io_port(dev->vm, port);
354         PrintError("Could not allocate io hook dev state\n");
355         return -1;
356     }
357     
358     return 0;
359 }
360
361
362 int v3_dev_unhook_io(struct vm_device * dev, uint16_t port) {
363     if (free_resource(dev, DEV_IO_HOOK, port) == 0) {
364         return v3_unhook_io_port(dev->vm, port);           
365     } 
366
367     return -1;
368 }
369
370
371 int v3_dev_hook_msr(struct vm_device * dev, uint32_t msr,
372                     int (*read)(struct guest_info * core, uint32_t msr, struct v3_msr * dst, void * priv_data),
373                     int (*write)(struct guest_info * core, uint32_t msr, struct v3_msr src, void * priv_data)) {
374     int ret = 0;
375
376     ret = v3_hook_msr(dev->vm, msr, read, write, dev->private_data);
377
378     if (ret == -1) {
379         return -1;
380     }
381
382     if (add_resource(dev, DEV_MSR_HOOK, msr) == -1) {
383         v3_unhook_msr(dev->vm, msr);
384         return -1;
385     }
386
387     return 0;
388 }
389                   
390 int v3_dev_unhook_msr(struct vm_device * dev, uint32_t msr) {
391     if (free_resource(dev, DEV_MSR_HOOK, msr) == 0) {
392         return v3_unhook_msr(dev->vm, msr);
393     }
394
395     return -1;
396 }
397
398
399
400
401 int v3_remove_device(struct vm_device * dev) {
402     struct vmm_dev_mgr * mgr = &(dev->vm->dev_mgr);
403     struct dev_rsrc * resource = NULL;
404     struct dev_rsrc * tmp;
405
406     list_for_each_entry_safe(resource, tmp, &(dev->res_hooks), node) {
407         if (resource->type == DEV_IO_HOOK) {
408             v3_unhook_io_port(dev->vm, (uint16_t)(resource->rsrc));
409         } else if (resource->type == DEV_MSR_HOOK) {
410             v3_unhook_msr(dev->vm, (uint32_t)(resource->rsrc));
411         }
412
413         list_del(&(resource->node));
414         V3_Free(resource);    
415     }
416
417     if (dev->ops->free) {
418         dev->ops->free(dev->private_data);
419     } else {
420         PrintError("Error: %s free() not implemented\n",  dev->name);
421     }
422
423     list_del(&(dev->dev_link));
424     mgr->num_devs--;
425
426     dev->vm = NULL;
427
428     V3_Free(dev);
429     return -1;
430 }
431
432
433 struct vm_device * v3_add_device(struct v3_vm_info * vm,
434                                  char * name, 
435                                  struct v3_device_ops * ops, 
436                                  void * private_data) {
437     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
438     struct vm_device * dev = NULL;
439
440
441     // Check if we already registered a device of the same name
442     if (v3_htable_search(mgr->dev_table, (addr_t)name) != (addr_t)NULL) {
443         PrintError("Device with name (%s) already registered with VM\n", name); 
444         return NULL;
445     }
446
447     dev = (struct vm_device *)V3_Malloc(sizeof(struct vm_device));
448
449     if (dev == NULL) {
450         return NULL;
451     }
452
453     INIT_LIST_HEAD(&(dev->res_hooks));
454
455     strncpy(dev->name, name, 32);
456     dev->ops = ops;
457     dev->private_data = private_data;
458
459     dev->vm = vm;
460
461     list_add(&(dev->dev_link), &(mgr->dev_list));
462     mgr->num_devs++;
463
464     v3_htable_insert(mgr->dev_table, (addr_t)(dev->name), (addr_t)dev);
465
466     return dev;
467 }
468
469
470 void v3_print_dev_mgr(struct v3_vm_info * vm) {
471     struct vmm_dev_mgr * mgr = &(vm->dev_mgr);
472     struct vm_device * dev;
473
474     V3_Print("%d devices registered with manager\n", mgr->num_devs);
475
476     list_for_each_entry(dev, &(mgr->dev_list), dev_link) {
477         V3_Print("Device: %s\n", dev->name);
478     }
479
480     return;
481 }
482
483
484
485
486 struct blk_frontend {
487     int (*connect)(struct v3_vm_info * vm, 
488                     void * frontend_data, 
489                     struct v3_dev_blk_ops * ops, 
490                     v3_cfg_tree_t * cfg, 
491                     void * priv_data);
492         
493
494     struct list_head blk_node;
495
496     void * priv_data;
497 };
498
499
500
501 int v3_dev_add_blk_frontend(struct v3_vm_info * vm, 
502                             char * name, 
503                             int (*connect)(struct v3_vm_info * vm, 
504                                             void * frontend_data, 
505                                             struct v3_dev_blk_ops * ops, 
506                                             v3_cfg_tree_t * cfg, 
507                                             void * priv_data), 
508                             void * priv_data) {
509
510     struct blk_frontend * frontend = NULL;
511
512     frontend = (struct blk_frontend *)V3_Malloc(sizeof(struct blk_frontend));
513     memset(frontend, 0, sizeof(struct blk_frontend));
514     
515     frontend->connect = connect;
516     frontend->priv_data = priv_data;
517         
518     list_add(&(frontend->blk_node), &(vm->dev_mgr.blk_list));
519     v3_htable_insert(vm->dev_mgr.blk_table, (addr_t)(name), (addr_t)frontend);
520
521     return 0;
522 }
523
524 int v3_dev_connect_blk(struct v3_vm_info * vm, 
525                        char * frontend_name, 
526                        struct v3_dev_blk_ops * ops, 
527                        v3_cfg_tree_t * cfg, 
528                        void * private_data) {
529
530     struct blk_frontend * frontend = NULL;
531
532     frontend = (struct blk_frontend *)v3_htable_search(vm->dev_mgr.blk_table,
533                                                        (addr_t)frontend_name);
534     
535     if (frontend == NULL) {
536         PrintError("Could not find frontend blk device %s\n", frontend_name);
537         return 0;
538     }
539
540     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
541         PrintError("Error connecting to block frontend %s\n", frontend_name);
542         return -1;
543     }
544
545     return 0;
546 }
547
548
549
550 struct net_frontend {
551     int (*connect)(struct v3_vm_info * vm, 
552                     void * frontend_data, 
553                     struct v3_dev_net_ops * ops, 
554                     v3_cfg_tree_t * cfg, 
555                     void * priv_data);
556     
557
558     struct list_head net_node;
559
560     void * priv_data;
561 };
562
563
564 int v3_dev_add_net_frontend(struct v3_vm_info * vm, 
565                             char * name, 
566                             int (*connect)(struct v3_vm_info * vm, 
567                                             void * frontend_data, 
568                                             struct v3_dev_net_ops * ops, 
569                                             v3_cfg_tree_t * cfg, 
570                                             void * private_data), 
571                             void * priv_data)
572 {
573     struct net_frontend * frontend = NULL;
574
575     frontend = (struct net_frontend *)V3_Malloc(sizeof(struct net_frontend));
576     memset(frontend, 0, sizeof(struct net_frontend));
577     
578     frontend->connect = connect;
579     frontend->priv_data = priv_data;
580         
581     list_add(&(frontend->net_node), &(vm->dev_mgr.net_list));
582     v3_htable_insert(vm->dev_mgr.net_table, (addr_t)(name), (addr_t)frontend);
583
584     return 0;
585 }
586
587
588 int v3_dev_connect_net(struct v3_vm_info * vm, 
589                        char * frontend_name, 
590                        struct v3_dev_net_ops * ops, 
591                        v3_cfg_tree_t * cfg, 
592                        void * private_data)
593 {
594     struct net_frontend * frontend = NULL;
595
596     frontend = (struct net_frontend *)v3_htable_search(vm->dev_mgr.net_table,
597                                                        (addr_t)frontend_name);
598     
599     if (frontend == NULL) {
600         PrintError("Could not find frontend net device %s\n", frontend_name);
601         return 0;
602     }
603
604     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
605         PrintError("Error connecting to net frontend %s\n", frontend_name);
606         return -1;
607     }
608
609     return 0;
610 }
611
612
613 struct cons_frontend {
614     int (*connect)(struct v3_vm_info * vm, 
615                    void * frontend_data, 
616                    struct v3_dev_console_ops * ops, 
617                    v3_cfg_tree_t * cfg, 
618                    void * priv_data);
619     
620
621     struct list_head cons_node;
622
623     void * priv_data;
624 };
625
626 int v3_dev_add_console_frontend(struct v3_vm_info * vm, 
627                                 char * name, 
628                                 int (*connect)(struct v3_vm_info * vm, 
629                                                void * frontend_data, 
630                                                struct v3_dev_console_ops * ops, 
631                                                v3_cfg_tree_t * cfg, 
632                                                void * private_data), 
633                                 void * priv_data)
634 {
635     struct cons_frontend * frontend = NULL;
636
637     frontend = (struct cons_frontend *)V3_Malloc(sizeof(struct cons_frontend));
638     memset(frontend, 0, sizeof(struct cons_frontend));
639     
640     frontend->connect = connect;
641     frontend->priv_data = priv_data;
642         
643     list_add(&(frontend->cons_node), &(vm->dev_mgr.cons_list));
644     v3_htable_insert(vm->dev_mgr.cons_table, (addr_t)(name), (addr_t)frontend);
645
646     return 0;
647 }
648
649
650 int v3_dev_connect_console(struct v3_vm_info * vm, 
651                            char * frontend_name, 
652                            struct v3_dev_console_ops * ops, 
653                            v3_cfg_tree_t * cfg, 
654                            void * private_data)
655 {
656     struct cons_frontend * frontend = NULL;
657
658     frontend = (struct cons_frontend *)v3_htable_search(vm->dev_mgr.cons_table,
659                                                         (addr_t)frontend_name);
660     
661     if (frontend == NULL) {
662         PrintError("Could not find frontend console device %s\n", frontend_name);
663         return 0;
664     }
665     
666     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data) == -1) {
667         PrintError("Error connecting to console frontend %s\n", frontend_name);
668         return -1;
669     }
670
671     return 0;
672 }
673
674 struct char_frontend {
675     int (*connect)(struct v3_vm_info * vm, 
676                    void * frontend_data, 
677                    struct v3_dev_char_ops * ops, 
678                    v3_cfg_tree_t * cfg, 
679                    void * priv_data, 
680                    void ** push_fn_arg);
681     
682
683     struct list_head char_node;
684
685     void * priv_data;
686 };
687
688 int v3_dev_add_char_frontend(struct v3_vm_info * vm, 
689                              char * name, 
690                              int (*connect)(struct v3_vm_info * vm, 
691                                             void * frontend_data, 
692                                             struct v3_dev_char_ops * ops, 
693                                             v3_cfg_tree_t * cfg, 
694                                             void * private_data, 
695                                             void ** push_fn_arg), 
696                              void * priv_data)
697 {
698     struct char_frontend * frontend = NULL;
699
700     frontend = (struct char_frontend *)V3_Malloc(sizeof(struct char_frontend));
701     memset(frontend, 0, sizeof(struct char_frontend));
702     
703     frontend->connect = connect;
704     frontend->priv_data = priv_data;
705         
706     list_add(&(frontend->char_node), &(vm->dev_mgr.char_list));
707     v3_htable_insert(vm->dev_mgr.char_table, (addr_t)(name), (addr_t)frontend);
708
709     return 0;
710 }
711
712
713 int v3_dev_connect_char(struct v3_vm_info * vm, 
714                         char * frontend_name, 
715                         struct v3_dev_char_ops * ops, 
716                         v3_cfg_tree_t * cfg, 
717                         void * private_data, 
718                         void ** push_fn_arg)
719 {
720     struct char_frontend * frontend = NULL;
721
722     frontend = (struct char_frontend *)v3_htable_search(vm->dev_mgr.char_table,
723                                                         (addr_t)frontend_name);
724     
725     if (frontend == NULL) {
726         PrintError("Could not find frontend char device %s\n", frontend_name);
727         return 0;
728     }
729     
730     if (frontend->connect(vm, frontend->priv_data, ops, cfg, private_data, push_fn_arg) == -1) {
731         PrintError("Error connecting to char frontend %s\n", frontend_name);
732         return -1;
733     }
734
735     return 0;
736 }
737
738
739
740 static int free_frontends(struct v3_vm_info * vm, struct vmm_dev_mgr * mgr) {
741     struct char_frontend * chr = NULL;
742     struct char_frontend * tmp_chr = NULL;
743     struct cons_frontend * cons = NULL;
744     struct cons_frontend * tmp_cons = NULL;
745     struct net_frontend * net = NULL;
746     struct net_frontend * tmp_net = NULL;
747     struct blk_frontend * blk = NULL;
748     struct blk_frontend * tmp_blk = NULL;
749
750
751
752     list_for_each_entry_safe(chr, tmp_chr, &(mgr->char_list), char_node) {
753         list_del(&(chr->char_node));
754         V3_Free(chr);
755     }
756
757     list_for_each_entry_safe(cons, tmp_cons, &(mgr->cons_list), cons_node) {
758         list_del(&(cons->cons_node));
759         V3_Free(cons);
760     }
761
762     list_for_each_entry_safe(net, tmp_net, &(mgr->net_list), net_node) {
763         list_del(&(net->net_node));
764         V3_Free(net);
765     }
766
767     list_for_each_entry_safe(blk, tmp_blk, &(mgr->blk_list), blk_node) {
768         list_del(&(blk->blk_node));
769         V3_Free(blk);
770     }
771
772     v3_free_htable(mgr->blk_table, 0, 0);
773     v3_free_htable(mgr->net_table, 0, 0);
774     v3_free_htable(mgr->char_table, 0, 0);
775     v3_free_htable(mgr->cons_table, 0, 0);
776
777
778     return 0;
779 }