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.


Constraints in page allocation, and code changes to use them; shadow paging allocati...
[palacios.git] / linux_module / palacios-stubs.c
1 #include <linux/kernel.h>
2 #include <linux/kthread.h>
3 #include <linux/spinlock.h>
4 #include <linux/gfp.h>
5 #include <linux/interrupt.h>
6 #include <linux/linkage.h>
7 #include <linux/sched.h>
8 #include <linux/uaccess.h>
9 #include <asm/irq_vectors.h>
10 #include <asm/io.h>
11
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/kthread.h>
15 #include <asm/uaccess.h>
16 #include <linux/smp.h>
17 #include <linux/vmalloc.h>
18
19 #include <palacios/vmm.h>
20 #include <palacios/vmm_host_events.h>
21 #include "palacios.h"
22
23 #include "mm.h"
24
25 #include "memcheck.h"
26 #include "lockcheck.h"
27
28 // The following can be used to track heap bugs
29 // zero memory after allocation
30 #define ALLOC_ZERO_MEM 0
31 // pad allocations by this many bytes on both ends of block
32 #define ALLOC_PAD      0
33
34
35 u32 pg_allocs = 0;
36 u32 pg_frees = 0;
37 u32 mallocs = 0;
38 u32 frees = 0;
39 u32 vmallocs = 0;
40 u32 vfrees = 0;
41
42 static struct v3_vm_info * irq_to_guest_map[256];
43
44
45 extern unsigned int cpu_khz;
46
47 extern int cpu_list[NR_CPUS];
48 extern int cpu_list_len;
49
50
51 static char *print_buffer[NR_CPUS];
52
53 static void deinit_print_buffers(void)
54 {
55     int i;
56
57     for (i=0;i<NR_CPUS;i++) {
58         if (print_buffer[i]) { 
59             palacios_free(print_buffer[i]);
60             print_buffer[i]=0;
61         }
62     }
63 }
64
65 static int init_print_buffers(void)
66 {
67     int i;
68     
69     memset(print_buffer,0,sizeof(char*)*NR_CPUS);
70
71 #if !V3_PRINTK_OLD_STYLE_OUTPUT
72
73     for (i=0;i<NR_CPUS;i++) { 
74         print_buffer[i] = palacios_alloc(V3_PRINTK_BUF_SIZE);
75         if (!print_buffer[i]) { 
76             ERROR("Cannot allocate print buffer for cpu %d\n",i);
77             deinit_print_buffers();
78             return -1;
79         }
80         memset(print_buffer[i],0,V3_PRINTK_BUF_SIZE);
81     }
82
83 #endif
84     
85     return 0;
86
87 }
88  
89 /**
90  * Prints a message to the console.
91  */
92 void palacios_print_scoped(void * vm, int vcore, const char *fmt, ...) {
93
94 #if V3_PRINTK_OLD_STYLE_OUTPUT
95
96   va_list ap;
97
98   va_start(ap, fmt);
99   vprintk(fmt, ap);
100   va_end(ap);
101
102   return
103
104 #else 
105
106   va_list ap;
107   char *buf;
108   unsigned int cpu = palacios_get_cpu();
109   struct v3_guest *guest = (struct v3_guest *)vm;
110
111   buf = print_buffer[cpu];
112
113   if (!buf) { 
114       printk(KERN_INFO "palacios (pcore %u): output skipped - no allocated buffer\n",cpu);
115       return;
116   } 
117
118   va_start(ap, fmt);
119   vsnprintf(buf,V3_PRINTK_BUF_SIZE, fmt, ap);
120   va_end(ap);
121
122 #if V3_PRINTK_CHECK_7BIT
123   {
124       char c=0;
125       int i;
126       for (i=0;i<strlen(buf);i++) { 
127           if (buf[i] < 0) {
128               c=buf[i];
129               break;
130           }
131       }
132       if (c!=0) { 
133           printk(KERN_INFO "palacios (pcore %u): ALERT ALERT 8 BIT CHAR (c=%d) DETECTED\n", cpu,c);
134       }
135   }
136 #endif
137
138   if (guest) {
139     if (vcore>=0) { 
140       printk(KERN_INFO "palacios (pcore %u vm %s vcore %u): %s",
141              cpu,
142              guest->name,
143              vcore,
144              buf);
145     } else {
146        printk(KERN_INFO "palacios (pcore %u vm %s): %s",
147              cpu,
148              guest->name,
149              buf);
150     }
151   } else {
152     printk(KERN_INFO "palacios (pcore %u): %s",
153            cpu,
154            buf);
155   }
156     
157   return;
158
159 #endif
160
161 }
162
163
164 /*
165  * Allocates a contiguous region of pages of the requested size.
166  * Returns the physical address of the first page in the region.
167  */
168 void *palacios_allocate_pages(int num_pages, unsigned int alignment, int node_id, int constraints) {
169     void * pg_addr = NULL;
170
171     if (num_pages<=0) { 
172       ERROR("ALERT ALERT Attempt to allocate zero or fewer pages\n");
173       return NULL;
174     }
175
176     pg_addr = (void *)alloc_palacios_pgs(num_pages, alignment, node_id, constraints);
177
178     if (!pg_addr) { 
179         ERROR("ALERT ALERT  Page allocation has FAILED Warning\n");
180         return NULL;
181     }
182
183     pg_allocs += num_pages;
184
185     MEMCHECK_ALLOC_PAGES(pg_addr,num_pages*4096);
186
187     return pg_addr;
188 }
189
190
191 /**
192  * Frees a page previously allocated via palacios_allocate_page().
193  * Note that palacios_allocate_page() can allocate multiple pages with
194  * a single call while palacios_free_page() only frees a single page.
195  */
196
197 void palacios_free_pages(void * page_paddr, int num_pages) {
198     pg_frees += num_pages;
199     free_palacios_pgs((uintptr_t)page_paddr, num_pages);
200     MEMCHECK_FREE_PAGES(page_paddr,num_pages*4096);
201
202 }
203
204
205 void *
206 palacios_alloc_extended(unsigned int size, unsigned int flags, int node) {
207     void * addr = NULL;
208
209     if (size==0) { 
210       // note that modern kernels will respond to a zero byte
211       // kmalloc and return the address 0x10...  In Palacios, 
212       // we will simply not allow 0 byte allocs at all, of any kind
213       ERROR("ALERT ALERT attempt to kmalloc zero bytes rejected\n");
214       return NULL;
215     }
216
217     if (node==-1) { 
218         addr = kmalloc(size+2*ALLOC_PAD, flags);
219     } else {
220         addr = kmalloc_node(size+2*ALLOC_PAD, flags, node);
221     }
222
223     if (!addr) { 
224        ERROR("ALERT ALERT  kmalloc has FAILED FAILED FAILED\n");
225        return NULL;
226     }   
227
228     mallocs++;
229
230 #if ALLOC_ZERO_MEM
231     memset(addr,0,size+2*ALLOC_PAD);
232 #endif
233
234     MEMCHECK_KMALLOC(addr,size+2*ALLOC_PAD);
235
236     return addr+ALLOC_PAD;
237 }
238
239 void *
240 palacios_valloc(unsigned int size)
241 {
242     void * addr = NULL;
243
244     if (size==0) { 
245       ERROR("ALERT ALERT attempt to vmalloc zero bytes rejected\n");
246       return NULL;
247     }
248
249     addr = vmalloc(size);
250
251     if (!addr) { 
252        ERROR("ALERT ALERT  vmalloc has FAILED FAILED FAILED\n");
253        return NULL;
254     }   
255
256     vmallocs++;
257
258     MEMCHECK_VMALLOC(addr,size);
259
260     return addr;
261 }
262
263 void palacios_vfree(void *p)
264 {
265   vfree(p);
266   vfrees++;
267   MEMCHECK_VFREE(p);
268 }
269
270 /**
271  * Allocates 'size' bytes of kernel memory.
272  * Returns the kernel virtual address of the memory allocated.
273  */
274 void *
275 palacios_alloc(unsigned int size) {
276
277     // It is very important that this test remains since 
278     // this function is used extensively throughout palacios and the linux
279     // module, both in places where interrupts are off and where they are on
280     // a GFP_KERNEL call, when done with interrupts off can lead to DEADLOCK
281     if (irqs_disabled()) {
282         return palacios_alloc_extended(size,GFP_ATOMIC,-1);
283     } else {
284         return palacios_alloc_extended(size,GFP_KERNEL,-1);
285     }
286
287 }
288
289 /**
290  * Frees memory that was previously allocated by palacios_alloc().
291  */
292 void
293 palacios_free(
294         void *                  addr
295 )
296 {
297     frees++;
298     kfree(addr-ALLOC_PAD);
299     MEMCHECK_KFREE(addr-ALLOC_PAD);
300 }
301
302 /**
303  * Converts a kernel virtual address to the corresponding physical address.
304  */
305 void *
306 palacios_vaddr_to_paddr(
307         void *                  vaddr
308 )
309 {
310     return (void*) __pa(vaddr);
311
312 }
313
314 /**
315  * Converts a physical address to the corresponding kernel virtual address.
316  */
317 void *
318 palacios_paddr_to_vaddr(
319         void *                  paddr
320 )
321 {
322   return __va(paddr);
323 }
324
325 /**
326  * Runs a function on the specified CPU.
327  */
328 static void 
329 palacios_xcall(
330         int                     cpu_id, 
331         void                    (*fn)(void *arg),
332         void *                  arg
333 )
334 {
335
336
337     // We set wait to 1, but I'm not sure this is necessary
338     smp_call_function_single(cpu_id, fn, arg, 1);
339     
340     return;
341 }
342
343
344 #define MAX_THREAD_NAME 32
345
346 struct lnx_thread_arg {
347     int (*fn)(void * arg);
348     void * arg;
349     char name[MAX_THREAD_NAME];
350 };
351
352 static int lnx_thread_target(void * arg) {
353     struct lnx_thread_arg * thread_info = (struct lnx_thread_arg *)arg;
354     int ret = 0;
355     /*
356       INFO("Daemonizing new Palacios thread (name=%s)\n", thread_info->name);
357
358       daemonize(thread_info->name);
359       allow_signal(SIGKILL);
360     */
361
362
363     ret = thread_info->fn(thread_info->arg);
364
365
366     INFO("Palacios Thread (%s) EXITING\n", thread_info->name);
367
368     palacios_free(thread_info);
369     // handle cleanup 
370
371     do_exit(ret);
372     
373     return 0; // should not get here.
374 }
375
376 /**
377  * Creates a kernel thread.
378  */
379 void *
380 palacios_start_kernel_thread(
381         int (*fn)               (void * arg),
382         void *                  arg,
383         char *                  thread_name) {
384
385     struct lnx_thread_arg * thread_info = palacios_alloc(sizeof(struct lnx_thread_arg));
386
387     if (!thread_info) { 
388         ERROR("ALERT ALERT Unable to allocate thread\n");
389         return NULL;
390     }
391
392     thread_info->fn = fn;
393     thread_info->arg = arg;
394     strncpy(thread_info->name,thread_name,MAX_THREAD_NAME);
395     thread_info->name[MAX_THREAD_NAME-1] =0;
396
397     return kthread_run( lnx_thread_target, thread_info, thread_info->name );
398 }
399
400
401 /**
402  * Starts a kernel thread on the specified CPU.
403  */
404 void * 
405 palacios_start_thread_on_cpu(int cpu_id, 
406                              int (*fn)(void * arg), 
407                              void * arg, 
408                              char * thread_name ) {
409     struct task_struct * thread = NULL;
410     struct lnx_thread_arg * thread_info = palacios_alloc(sizeof(struct lnx_thread_arg));
411
412     if (!thread_info) { 
413         ERROR("ALERT ALERT Unable to allocate thread to start on cpu\n");
414         return NULL;
415     }
416
417     thread_info->fn = fn;
418     thread_info->arg = arg;
419     strncpy(thread_info->name,thread_name,MAX_THREAD_NAME);
420     thread_info->name[MAX_THREAD_NAME-1] =0;
421
422     thread = kthread_create( lnx_thread_target, thread_info, thread_info->name );
423
424     if (IS_ERR(thread)) {
425         WARNING("Palacios error creating thread: %s\n", thread_info->name);
426         palacios_free(thread_info);
427         return NULL;
428     }
429
430     if (set_cpus_allowed_ptr(thread, cpumask_of(cpu_id)) != 0) {
431         WARNING("Attempt to start thread on disallowed CPU\n");
432         kthread_stop(thread);
433         palacios_free(thread_info);
434         return NULL;
435     }
436
437     wake_up_process(thread);
438
439     return thread;
440 }
441
442
443 /**
444  * Rebind a kernel thread to the specified CPU
445  * The thread will be running on target CPU on return
446  * non-zero return means failure
447  */
448 int
449 palacios_move_thread_to_cpu(int new_cpu_id, 
450                             void * thread_ptr) {
451     struct task_struct * thread = (struct task_struct *)thread_ptr;
452
453     INFO("Moving thread (%p) to cpu %d\n", thread, new_cpu_id);
454
455     if (thread == NULL) {
456         thread = current;
457     }
458
459     /*
460      * Bind to the specified CPU.  When this call returns,
461      * the thread should be running on the target CPU.
462      */
463     return set_cpus_allowed_ptr(thread, cpumask_of(new_cpu_id));
464 }
465
466
467 /**
468  * Returns the CPU ID that the caller is running on.
469  */
470 unsigned int 
471 palacios_get_cpu(void) 
472 {
473
474     /* We want to call smp_processor_id()
475      * But this is not safe if kernel preemption is possible 
476      * We need to ensure that the palacios threads are bound to a give cpu
477      */
478
479     unsigned int cpu_id = get_cpu(); 
480     put_cpu();
481     return cpu_id;
482 }
483
484 /**
485  * Interrupts the physical CPU corresponding to the specified logical guest cpu.
486  *
487  * NOTE: 
488  * This is dependent on the implementation of xcall_reschedule().  Currently
489  * xcall_reschedule does not explicitly call schedule() on the destination CPU,
490  * but instead relies on the return to user space to handle it. Because
491  * palacios is a kernel thread schedule will not be called, which is correct.
492  * If it ever changes to induce side effects, we'll need to figure something
493  * else out...
494  */
495
496 #include <asm/apic.h>
497
498 static void
499 palacios_interrupt_cpu(
500         struct v3_vm_info *     vm, 
501         int                     cpu_id, 
502         int                     vector
503 )
504 {
505     if (vector == 0) {
506         smp_send_reschedule(cpu_id);
507     } else {
508         apic->send_IPI_mask(cpumask_of(cpu_id), vector);
509     }
510     return;
511 }
512
513 /**
514  * Dispatches an interrupt to Palacios for handling.
515  */
516 static void
517 palacios_dispatch_interrupt( int vector, void * dev, struct pt_regs * regs ) {
518     struct v3_interrupt intr = {
519         .irq            = vector,
520         .error          = regs->orig_ax,
521         .should_ack     = 1,
522     };
523     
524     if (irq_to_guest_map[vector]) {
525         v3_deliver_irq(irq_to_guest_map[vector], &intr);
526     }
527     
528 }
529
530 /**
531  * Instructs the kernel to forward the specified IRQ to Palacios.
532  */
533 static int
534 palacios_hook_interrupt(struct v3_vm_info *     vm,
535                         unsigned int            vector ) {
536     INFO("hooking vector %d\n", vector);        
537
538     if (irq_to_guest_map[vector]) {
539         WARNING(
540                "%s: Interrupt vector %u is already hooked.\n",
541                __func__, vector);
542         return -1;
543     }
544
545     DEBUG(
546            "%s: Hooking interrupt vector %u to vm %p.\n",
547            __func__, vector, vm);
548
549     irq_to_guest_map[vector] = vm;
550
551     /*
552      * NOTE: Normally PCI devices are supposed to be level sensitive,
553      *       but we need them to be edge sensitive so that they are
554      *       properly latched by Palacios.  Leaving them as level
555      *       sensitive would lead to an interrupt storm.
556      */
557     //ioapic_set_trigger_for_vector(vector, ioapic_edge_sensitive);
558     
559     //set_idtvec_handler(vector, palacios_dispatch_interrupt);
560     if (vector < 32) {
561         ERROR("unexpected vector for hooking\n");
562         return -1;
563     } else {
564         int device_id = 0;              
565         
566         int flag = 0;
567         int error;
568                 
569         DEBUG("hooking vector: %d\n", vector);          
570
571         if (vector == 32) {
572             flag = IRQF_TIMER;
573         } else {
574             flag = IRQF_SHARED;
575         }
576
577         error = request_irq((vector - 32),
578                             (void *)palacios_dispatch_interrupt,
579                             flag,
580                             "interrupt_for_palacios",
581                             &device_id);
582         
583         if (error) {
584             ERROR("error code for request_irq is %d\n", error);
585             ERROR("request vector %d failed", vector);
586             return -1;
587         }
588     }
589         
590     return 0;
591 }
592
593
594
595 /**
596  * Acknowledges an interrupt.
597  */
598 static int
599 palacios_ack_interrupt(
600         int                     vector
601
602 {
603   ack_APIC_irq(); 
604   DEBUG("Pretending to ack interrupt, vector=%d\n", vector);
605   return 0;
606 }
607   
608 /**
609  * Returns the CPU frequency in kilohertz.
610  */
611 unsigned int
612 palacios_get_cpu_khz(void) 
613 {
614     INFO("cpu_khz is %u\n", cpu_khz);
615
616     if (cpu_khz == 0) { 
617         INFO("faking cpu_khz to 1000000\n");
618         return 1000000;
619     } else {
620         return cpu_khz;
621     }
622   //return 1000000;
623 }
624
625 /**
626  * Yield the CPU so other host OS tasks can run.
627  * This will return immediately if there is no other thread that is runnable
628  * And there is no real bound on how long it will yield
629  */
630 void
631 palacios_yield_cpu(void)
632 {
633     schedule();
634     return;
635 }
636
637 /**
638  * Yield the CPU so other host OS tasks can run.
639  * Given now immediately if there is no other thread that is runnable
640  * And there is no real bound on how long it will yield
641  */
642 void palacios_sleep_cpu(unsigned int us)
643 {
644
645     set_current_state(TASK_INTERRUPTIBLE);
646     if (us) {
647         unsigned int uspj = 1000000U/HZ;
648         unsigned int jiffies = us/uspj + ((us%uspj) !=0);  // ceiling 
649         schedule_timeout(jiffies);
650     } else {
651         schedule();
652     }
653     return;
654 }
655
656 void palacios_wakeup_cpu(void *thread)
657 {
658     wake_up_process(thread);
659     return;
660 }
661
662 /**
663  * Allocates a mutex.
664  * Returns NULL on failure.
665  */
666 void *
667 palacios_mutex_alloc(void)
668 {
669     spinlock_t *lock = palacios_alloc(sizeof(spinlock_t));
670
671     if (lock) {
672         spin_lock_init(lock);
673         LOCKCHECK_ALLOC(lock);
674     } else {
675         ERROR("ALERT ALERT Unable to allocate lock\n");
676         return NULL;
677     }
678     
679     return lock;
680 }
681
682 void palacios_mutex_init(void *mutex)
683 {
684   spinlock_t *lock = (spinlock_t*)mutex;
685   
686   if (lock) {
687     spin_lock_init(lock);
688     LOCKCHECK_ALLOC(lock);
689   }
690 }
691
692 void palacios_mutex_deinit(void *mutex)
693 {
694   spinlock_t *lock = (spinlock_t*)mutex;
695   
696   if (lock) {
697     // no actual spin_lock_deinit on linux
698     // our purpose here is to drive the lock checker
699     LOCKCHECK_FREE(lock);
700   }
701 }
702
703
704 /**
705  * Frees a mutex.
706  */
707 void
708 palacios_mutex_free(void * mutex) {
709     palacios_free(mutex);
710     LOCKCHECK_FREE(mutex);
711 }
712
713 /**
714  * Locks a mutex.
715  */
716 void 
717 palacios_mutex_lock(void * mutex, int must_spin) {
718
719     LOCKCHECK_LOCK_PRE(mutex);
720     spin_lock((spinlock_t *)mutex);
721     LOCKCHECK_LOCK_POST(mutex);
722 }
723
724
725 /**
726  * Locks a mutex, disabling interrupts on this core
727  */
728 void *
729 palacios_mutex_lock_irqsave(void * mutex, int must_spin) {
730     
731     unsigned long flags; 
732     
733     LOCKCHECK_LOCK_IRQSAVE_PRE(mutex,flags);
734     spin_lock_irqsave((spinlock_t *)mutex,flags);
735     LOCKCHECK_LOCK_IRQSAVE_POST(mutex,flags);
736
737     return (void *)flags;
738 }
739
740
741 /**
742  * Unlocks a mutex.
743  */
744 void 
745 palacios_mutex_unlock(
746         void *                  mutex
747
748 {
749     LOCKCHECK_UNLOCK_PRE(mutex);
750     spin_unlock((spinlock_t *)mutex);
751     LOCKCHECK_UNLOCK_POST(mutex);
752 }
753
754
755 /**
756  * Unlocks a mutex and restores previous interrupt state on this core
757  */
758 void 
759 palacios_mutex_unlock_irqrestore(void *mutex, void *flags)
760 {
761     LOCKCHECK_UNLOCK_IRQRESTORE_PRE(mutex,(unsigned long)flags);
762     // This is correct, flags is opaque
763     spin_unlock_irqrestore((spinlock_t *)mutex,(unsigned long)flags);
764     LOCKCHECK_UNLOCK_IRQRESTORE_POST(mutex,(unsigned long)flags);
765 }
766
767 /**
768  * Structure used by the Palacios hypervisor to interface with the host kernel.
769  */
770 static struct v3_os_hooks palacios_os_hooks = {
771         .print                  = palacios_print_scoped,
772         .allocate_pages         = palacios_allocate_pages,
773         .free_pages             = palacios_free_pages,
774         .malloc                 = palacios_alloc,
775         .free                   = palacios_free,
776         .vaddr_to_paddr         = palacios_vaddr_to_paddr,
777         .paddr_to_vaddr         = palacios_paddr_to_vaddr,
778         .hook_interrupt         = palacios_hook_interrupt,
779         .ack_irq                = palacios_ack_interrupt,
780         .get_cpu_khz            = palacios_get_cpu_khz,
781         .start_kernel_thread    = palacios_start_kernel_thread,
782         .yield_cpu              = palacios_yield_cpu,
783         .sleep_cpu              = palacios_sleep_cpu,
784         .wakeup_cpu             = palacios_wakeup_cpu,
785         .mutex_alloc            = palacios_mutex_alloc,
786         .mutex_free             = palacios_mutex_free,
787         .mutex_lock             = palacios_mutex_lock, 
788         .mutex_unlock           = palacios_mutex_unlock,
789         .mutex_lock_irqsave     = palacios_mutex_lock_irqsave, 
790         .mutex_unlock_irqrestore= palacios_mutex_unlock_irqrestore,
791         .get_cpu                = palacios_get_cpu,
792         .interrupt_cpu          = palacios_interrupt_cpu,
793         .call_on_cpu            = palacios_xcall,
794         .start_thread_on_cpu    = palacios_start_thread_on_cpu,
795         .move_thread_to_cpu     = palacios_move_thread_to_cpu,
796 };
797
798
799
800
801 int palacios_vmm_init( char *options )
802 {
803     int num_cpus = num_online_cpus();
804     char * cpu_mask = NULL;
805
806     if (cpu_list_len > 0) {
807         int major = 0;
808         int minor = 0;
809         int i = 0;
810
811         cpu_mask = palacios_alloc((num_cpus / 8) + 1);
812
813         if (!cpu_mask) { 
814             ERROR("Cannot allocate cpu mask\n");
815             return -1;
816         }
817
818         memset(cpu_mask, 0, (num_cpus / 8) + 1);
819         
820         for (i = 0; i < cpu_list_len; i++) {
821             if (cpu_list[i] >= num_cpus) {
822                 WARNING("CPU (%d) exceeds number of available CPUs. Ignoring...\n", cpu_list[i]);
823                 continue;
824             }
825
826             major = cpu_list[i] / 8;
827             minor = cpu_list[i] % 8;
828     
829             *(cpu_mask + major) |= (0x1 << minor);
830         }
831     }
832
833     memset(irq_to_guest_map, 0, sizeof(struct v3_vm_info *) * 256);
834
835     if (init_print_buffers()) {
836         ERROR("Cannot initialize print buffers\n");
837         palacios_free(cpu_mask);
838         return -1;
839     }
840
841     INFO("palacios_init starting - calling init_v3\n");
842
843     Init_V3(&palacios_os_hooks, cpu_mask, num_cpus, options);
844
845     return 0;
846
847 }
848
849
850 int palacios_vmm_exit( void ) {
851
852     Shutdown_V3();
853
854     INFO("palacios shutdown complete\n");
855
856     deinit_print_buffers();
857
858     return 0;
859 }