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.


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