1 /*
2  * Cisco router simulation platform.
3  * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4  *
5  * Virtual machine abstraction.
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <assert.h>
16 #include <glob.h>
17 
18 #include "registry.h"
19 #include "device.h"
20 #include "pci_dev.h"
21 #include "pci_io.h"
22 #include "cpu.h"
23 #include "vm.h"
24 #include "tcb.h"
25 #include "mips64_jit.h"
26 #include "dev_vtty.h"
27 
28 #include MIPS64_ARCH_INC_FILE
29 
30 #define DEBUG_VM  1
31 
32 #define VM_GLOCK()   pthread_mutex_lock(&vm_global_lock)
33 #define VM_GUNLOCK() pthread_mutex_unlock(&vm_global_lock)
34 
35 /* Type of VM file naming (0=use VM name, 1=use instance ID) */
36 int vm_file_naming_type = 0;
37 
38 /* Platform list */
39 static struct vm_platform_list *vm_platforms = NULL;
40 
41 /* Pool of ghost images */
42 static vm_ghost_image_t *vm_ghost_pool = NULL;
43 
44 /* Global lock for VM manipulation */
45 static pthread_mutex_t vm_global_lock = PTHREAD_MUTEX_INITIALIZER;
46 
47 /* Free all chunks used by a VM */
48 static void vm_chunk_free_all(vm_instance_t *vm);
49 
50 /* Initialize a VM object */
vm_object_init(vm_obj_t * obj)51 void vm_object_init(vm_obj_t *obj)
52 {
53    memset(obj,0,sizeof(*obj));
54 }
55 
56 /* Add a VM object to an instance */
vm_object_add(vm_instance_t * vm,vm_obj_t * obj)57 void vm_object_add(vm_instance_t *vm,vm_obj_t *obj)
58 {
59    obj->next = vm->vm_object_list;
60    obj->pprev = &vm->vm_object_list;
61 
62    if (vm->vm_object_list)
63       vm->vm_object_list->pprev = &obj->next;
64 
65    vm->vm_object_list = obj;
66 }
67 
68 /* Remove a VM object from an instance */
vm_object_remove(vm_instance_t * vm,vm_obj_t * obj)69 void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj)
70 {
71    if (obj->next)
72       obj->next->pprev = obj->pprev;
73    *(obj->pprev) = obj->next;
74 
75    obj->shutdown(vm,obj->data);
76 }
77 
78 /* Find an object given its name */
vm_object_find(vm_instance_t * vm,char * name)79 vm_obj_t *vm_object_find(vm_instance_t *vm,char *name)
80 {
81    vm_obj_t *obj;
82 
83    for(obj=vm->vm_object_list;obj;obj=obj->next)
84       if (!strcmp(obj->name,name))
85          return obj;
86 
87    return NULL;
88 }
89 
90 /* Check that a mandatory object is present */
vm_object_check(vm_instance_t * vm,char * name)91 int vm_object_check(vm_instance_t *vm,char *name)
92 {
93    return(vm_object_find(vm,name) ? 0 : -1);
94 }
95 
96 /* Shut down all objects of an instance */
vm_object_free_list(vm_instance_t * vm)97 void vm_object_free_list(vm_instance_t *vm)
98 {
99    vm_obj_t *obj,*next;
100 
101    for(obj=vm->vm_object_list;obj;obj=next) {
102       next = obj->next;
103 
104       if (obj->shutdown != NULL) {
105 #if DEBUG_VM
106          vm_log(vm,"VM_OBJECT","Shutdown of object \"%s\"\n",obj->name);
107 #endif
108          obj->shutdown(vm,obj->data);
109       }
110    }
111 
112    vm->vm_object_list = NULL;
113 }
114 
115 /* Rebuild the object list pointers */
vm_object_rebuild_list(vm_instance_t * vm)116 _Unused static void vm_object_rebuild_list(vm_instance_t *vm)
117 {
118    vm_obj_t **obj;
119 
120    for(obj=&vm->vm_object_list;*obj;obj=&(*obj)->next)
121       (*obj)->pprev = obj;
122 }
123 
124 /* Dump the object list of an instance */
vm_object_dump(vm_instance_t * vm)125 void vm_object_dump(vm_instance_t *vm)
126 {
127    vm_obj_t *obj;
128 
129    printf("VM \"%s\" (%u) object list:\n",vm->name,vm->instance_id);
130 
131    for(obj=vm->vm_object_list;obj;obj=obj->next) {
132       printf("  - %-15s [data=%p]\n",obj->name,obj->data);
133    }
134 
135    printf("\n");
136 }
137 
138 /* Get VM type */
vm_get_type(vm_instance_t * vm)139 char *vm_get_type(vm_instance_t *vm)
140 {
141    return vm->platform->name;
142 }
143 
144 /* Get log name */
vm_get_log_name(vm_instance_t * vm)145 static char *vm_get_log_name(vm_instance_t *vm)
146 {
147    if (vm->platform->log_name != NULL)
148       return vm->platform->log_name;
149 
150    /* default value */
151    return "VM";
152 }
153 
154 /* Get MAC address MSB */
vm_get_mac_addr_msb(vm_instance_t * vm)155 u_int vm_get_mac_addr_msb(vm_instance_t *vm)
156 {
157    if (vm->platform->get_mac_addr_msb != NULL)
158       return(vm->platform->get_mac_addr_msb());
159 
160    /* default value */
161    return(0xC6);
162 }
163 
164 /* Generate a filename for use by the instance */
vm_build_filename(vm_instance_t * vm,char * name)165 char *vm_build_filename(vm_instance_t *vm,char *name)
166 {
167    char *filename,*machine;
168 
169    machine = vm_get_type(vm);
170 
171    switch(vm_file_naming_type) {
172       case 1:
173          filename = dyn_sprintf("%s_i%u_%s",machine,vm->instance_id,name);
174          break;
175       case 0:
176       default:
177          filename = dyn_sprintf("%s_%s_%s",machine,vm->name,name);
178          break;
179    }
180 
181    assert(filename != NULL);
182    return filename;
183 }
184 
185 /* Get the amount of host virtual memory used by a VM */
vm_get_vspace_size(vm_instance_t * vm)186 size_t vm_get_vspace_size(vm_instance_t *vm)
187 {
188    struct vdevice *dev;
189    size_t hsize = 0;
190 
191    /* Add memory used by CPU (exec area) */
192    /* XXX TODO */
193 
194    /* Add memory used by devices */
195    for(dev=vm->dev_list;dev;dev=dev->next)
196       hsize += dev_get_vspace_size(dev);
197 
198    return(hsize);
199 }
200 
201 /* Erase lock file */
vm_release_lock(vm_instance_t * vm,int erase)202 void vm_release_lock(vm_instance_t *vm,int erase)
203 {
204    if (vm->lock_fd != NULL) {
205       fclose(vm->lock_fd);
206       vm->lock_fd = NULL;
207    }
208 
209    if (vm->lock_file != NULL) {
210       if (erase)
211          unlink(vm->lock_file);
212       free(vm->lock_file);
213       vm->lock_file = NULL;
214    }
215 }
216 
217 /* Check that an instance lock file doesn't already exist */
vm_get_lock(vm_instance_t * vm)218 int vm_get_lock(vm_instance_t *vm)
219 {
220    char pid_str[32];
221    struct flock lock;
222 
223    vm->lock_file = vm_build_filename(vm,"lock");
224 
225    if (!(vm->lock_fd = fopen(vm->lock_file,"w"))) {
226       fprintf(stderr,"Unable to create lock file \"%s\".\n",vm->lock_file);
227       return(-1);
228    }
229 
230    memset(&lock,0,sizeof(lock));
231    lock.l_type   = F_WRLCK;
232    lock.l_whence = SEEK_SET;
233    lock.l_start  = 0;
234    lock.l_len    = 0;
235 
236    if (fcntl(fileno(vm->lock_fd),F_SETLK,&lock) == -1) {
237       if (fcntl(fileno(vm->lock_fd),F_GETLK,&lock) == 0) {
238          snprintf(pid_str,sizeof(pid_str),"%ld",(long)lock.l_pid);
239       } else {
240          strcpy(pid_str,"unknown");
241       }
242 
243       fprintf(stderr,
244               "\nAn emulator instance (PID %s) is already running with "
245               "identifier %u.\n"
246               "If this is not the case, please erase file \"%s\".\n\n",
247               pid_str,vm->instance_id,vm->lock_file);
248       vm_release_lock(vm,FALSE);
249       return(-1);
250    }
251 
252    /* write the emulator PID */
253    fprintf(vm->lock_fd,"%ld\n",(u_long)getpid());
254    return(0);
255 }
256 
257 /* Log a message */
vm_flog(vm_instance_t * vm,char * module,char * format,va_list ap)258 void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap)
259 {
260    if (vm->log_fd)
261       m_flog(vm->log_fd,module,format,ap);
262 }
263 
264 /* Log a message */
vm_log(vm_instance_t * vm,char * module,char * format,...)265 void vm_log(vm_instance_t *vm,char *module,char *format,...)
266 {
267    va_list ap;
268 
269    if (vm->log_fd) {
270       va_start(ap,format);
271       vm_flog(vm,module,format,ap);
272       va_end(ap);
273    }
274 }
275 
276 /* Close the log file */
vm_close_log(vm_instance_t * vm)277 int vm_close_log(vm_instance_t *vm)
278 {
279    if (vm->log_fd)
280       fclose(vm->log_fd);
281 
282    free(vm->log_file);
283 
284    vm->log_file = NULL;
285    vm->log_fd = NULL;
286    return(0);
287 }
288 
289 /* Create the log file */
vm_create_log(vm_instance_t * vm)290 int vm_create_log(vm_instance_t *vm)
291 {
292    if (vm->log_file_enabled) {
293       vm_close_log(vm);
294 
295       if (!(vm->log_file = vm_build_filename(vm,"log.txt")))
296          return(-1);
297 
298       if (!(vm->log_fd = fopen(vm->log_file,"w"))) {
299          fprintf(stderr,"VM %s: unable to create log file '%s'\n",
300                  vm->name,vm->log_file);
301          free(vm->log_file);
302          vm->log_file = NULL;
303          return(-1);
304       }
305    }
306 
307    return(0);
308 }
309 
310 /* Reopen the log file */
vm_reopen_log(vm_instance_t * vm)311 int vm_reopen_log(vm_instance_t *vm)
312 {
313    if (vm->log_file_enabled) {
314       vm_close_log(vm);
315 
316       if (!(vm->log_file = vm_build_filename(vm,"log.txt")))
317          return(-1);
318 
319       if (!(vm->log_fd = fopen(vm->log_file,"a"))) {
320          fprintf(stderr,"VM %s: unable to reopen log file '%s'\n",
321                  vm->name,vm->log_file);
322          free(vm->log_file);
323          vm->log_file = NULL;
324          return(-1);
325       }
326    }
327 
328    return(0);
329 }
330 
331 /* Error message */
vm_error(vm_instance_t * vm,char * format,...)332 void vm_error(vm_instance_t *vm,char *format,...)
333 {
334    char buffer[2048];
335    va_list ap;
336 
337    va_start(ap,format);
338    vsnprintf(buffer,sizeof(buffer),format,ap);
339    va_end(ap);
340 
341    fprintf(stderr,"%s '%s': %s",vm_get_log_name(vm),vm->name,buffer);
342 }
343 
344 /* Create a new VM instance */
vm_create(char * name,int instance_id,vm_platform_t * platform)345 static vm_instance_t *vm_create(char *name,int instance_id,
346                                 vm_platform_t *platform)
347 {
348    vm_instance_t *vm;
349 
350    if (!(vm = malloc(sizeof(*vm)))) {
351       fprintf(stderr,"VM %s: unable to create new instance!\n",name);
352       return NULL;
353    }
354 
355    memset(vm,0,sizeof(*vm));
356 
357    if (!(vm->name = strdup(name))) {
358       fprintf(stderr,"VM %s: unable to store instance name!\n",name);
359       goto err_name;
360    }
361 
362    vm->instance_id          = instance_id;
363    vm->platform             = platform;
364    vm->status               = VM_STATUS_HALTED;
365    vm->jit_use              = JIT_SUPPORT;
366    vm->exec_blk_direct_jump = TRUE;
367    vm->vtty_con_type        = VTTY_TYPE_TERM;
368    vm->vtty_aux_type        = VTTY_TYPE_NONE;
369    vm->timer_irq_check_itv  = VM_TIMER_IRQ_CHECK_ITV;
370    vm->log_file_enabled     = TRUE;
371    vm->rommon_vars.filename = vm_build_filename(vm,"rommon_vars");
372 
373    if (!vm->rommon_vars.filename)
374       goto err_rommon;
375 
376    /* XXX */
377    rommon_load_file(&vm->rommon_vars);
378 
379    /* create lock file */
380    if (vm_get_lock(vm) == -1)
381       goto err_lock;
382 
383    /* create log file */
384    if (vm_create_log(vm) == -1)
385       goto err_log;
386 
387    if (registry_add(vm->name,OBJ_TYPE_VM,vm) == -1) {
388       fprintf(stderr,"VM: Unable to store instance '%s' in registry!\n",
389               vm->name);
390       goto err_reg_add;
391    }
392 
393    m_log("VM","VM %s created.\n",vm->name);
394    return vm;
395 
396  err_reg_add:
397    vm_close_log(vm);
398  err_log:
399    free(vm->lock_file);
400  err_lock:
401    free(vm->rommon_vars.filename);
402  err_rommon:
403    free(vm->name);
404  err_name:
405    free(vm);
406    return NULL;
407 }
408 
409 /*
410  * Shutdown hardware resources used by a VM.
411  * The CPU must have been stopped.
412  */
vm_hardware_shutdown(vm_instance_t * vm)413 int vm_hardware_shutdown(vm_instance_t *vm)
414 {
415    int i;
416 
417    if ((vm->status == VM_STATUS_HALTED) || !vm->cpu_group) {
418       vm_log(vm,"VM","trying to shutdown an inactive VM.\n");
419       return(-1);
420    }
421 
422    vm_log(vm,"VM","shutdown procedure engaged.\n");
423 
424    /* Mark the VM as halted */
425    vm->status = VM_STATUS_HALTED;
426 
427    /* Free the object list */
428    vm_object_free_list(vm);
429 
430    /* Free resources used by PCI busses */
431    vm_log(vm,"VM","removing PCI busses.\n");
432    pci_io_data_remove(vm,vm->pci_io_space);
433    pci_bus_remove(vm->pci_bus[0]);
434    pci_bus_remove(vm->pci_bus[1]);
435    vm->pci_bus[0] = vm->pci_bus[1] = NULL;
436 
437    /* Free the PCI bus pool */
438    for(i=0;i<VM_PCI_POOL_SIZE;i++) {
439       if (vm->pci_bus_pool[i] != NULL) {
440          pci_bus_remove(vm->pci_bus_pool[i]);
441          vm->pci_bus_pool[i] = NULL;
442       }
443    }
444 
445    /* Remove the IRQ routing vectors */
446    vm->set_irq = NULL;
447    vm->clear_irq = NULL;
448 
449    /* Delete the VTTY for Console and AUX ports */
450    vm_log(vm,"VM","deleting VTTY.\n");
451    vm_delete_vtty(vm);
452 
453    /* Delete system CPU group */
454    vm_log(vm,"VM","deleting system CPUs.\n");
455    cpu_group_delete(vm->cpu_group);
456    vm->cpu_group = NULL;
457    vm->boot_cpu = NULL;
458 
459    vm_log(vm,"VM","shutdown procedure completed.\n");
460    m_log("VM","VM %s shutdown.\n",vm->name);
461    return(0);
462 }
463 
464 /* Free resources used by a VM */
vm_free(vm_instance_t * vm)465 void vm_free(vm_instance_t *vm)
466 {
467    if (vm != NULL) {
468       /* Free hardware resources */
469       vm_hardware_shutdown(vm);
470 
471       m_log("VM","VM %s destroyed.\n",vm->name);
472 
473       /* Close log file */
474       vm_close_log(vm);
475 
476       /* Remove the lock file */
477       vm_release_lock(vm,TRUE);
478 
479       /* Free all chunks */
480       vm_chunk_free_all(vm);
481 
482       /* Free various elements */
483       free(vm->rommon_vars.filename);
484       free(vm->ghost_ram_filename);
485       free(vm->sym_filename);
486       free(vm->ios_image);
487       free(vm->ios_startup_config);
488       free(vm->ios_private_config);
489       free(vm->rom_filename);
490       free(vm->name);
491       free(vm);
492    }
493 }
494 
495 /* Get an instance given a name */
vm_acquire(char * name)496 vm_instance_t *vm_acquire(char *name)
497 {
498    return(registry_find(name,OBJ_TYPE_VM));
499 }
500 
501 /* Release a VM (decrement reference count) */
vm_release(vm_instance_t * vm)502 int vm_release(vm_instance_t *vm)
503 {
504    return(registry_unref(vm->name,OBJ_TYPE_VM));
505 }
506 
507 /* Initialize RAM */
vm_ram_init(vm_instance_t * vm,m_uint64_t paddr)508 int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr)
509 {
510    m_uint32_t len;
511 
512    len = vm->ram_size * 1048576;
513 
514    if (vm->ghost_status == VM_GHOST_RAM_USE) {
515       return(dev_ram_ghost_init(vm,"ram",vm->sparse_mem,vm->ghost_ram_filename,
516                                 paddr,len));
517    }
518 
519    return(dev_ram_init(vm,"ram",vm->ram_mmap,
520                        (vm->ghost_status != VM_GHOST_RAM_GENERATE),
521                        vm->ghost_ram_filename,vm->sparse_mem,paddr,len));
522 }
523 
524 /* Initialize VTTY */
vm_init_vtty(vm_instance_t * vm)525 int vm_init_vtty(vm_instance_t *vm)
526 {
527    /* Create Console and AUX ports */
528    vm->vtty_con = vtty_create(vm,"Console port",
529                               vm->vtty_con_type,vm->vtty_con_tcp_port,
530                               &vm->vtty_con_serial_option);
531 
532    vm->vtty_aux = vtty_create(vm,"AUX port",
533                               vm->vtty_aux_type,vm->vtty_aux_tcp_port,
534                               &vm->vtty_aux_serial_option);
535    return(0);
536 }
537 
538 /* Delete VTTY */
vm_delete_vtty(vm_instance_t * vm)539 void vm_delete_vtty(vm_instance_t *vm)
540 {
541    vtty_delete(vm->vtty_con);
542    vtty_delete(vm->vtty_aux);
543    vm->vtty_con = vm->vtty_aux = NULL;
544 }
545 
546 /* Bind a device to a virtual machine */
vm_bind_device(vm_instance_t * vm,struct vdevice * dev)547 int vm_bind_device(vm_instance_t *vm,struct vdevice *dev)
548 {
549    struct vdevice **cur;
550    u_int i;
551 
552    /*
553     * Add this device to the device array. The index in the device array
554     * is used by the MTS subsystem.
555     */
556    for(i=0;i<VM_DEVICE_MAX;i++)
557       if (!vm->dev_array[i])
558          break;
559 
560    if (i == VM_DEVICE_MAX) {
561       fprintf(stderr,"VM%u: vm_bind_device: device table full.\n",
562               vm->instance_id);
563       return(-1);
564    }
565 
566    vm->dev_array[i] = dev;
567    dev->id = i;
568 
569    /*
570     * Add it to the linked-list (devices are ordered by physical addresses).
571     */
572    for(cur=&vm->dev_list;*cur;cur=&(*cur)->next)
573       if ((*cur)->phys_addr > dev->phys_addr)
574          break;
575 
576    dev->next = *cur;
577    if (*cur) (*cur)->pprev = &dev->next;
578    dev->pprev = cur;
579    *cur = dev;
580    return(0);
581 }
582 
583 /* Unbind a device from a virtual machine */
vm_unbind_device(vm_instance_t * vm,struct vdevice * dev)584 int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev)
585 {
586    u_int i;
587 
588    if (!dev || !dev->pprev)
589       return(-1);
590 
591    /* Remove the device from the linked list */
592    if (dev->next)
593       dev->next->pprev = dev->pprev;
594 
595    *(dev->pprev) = dev->next;
596 
597    /* Remove the device from the device array */
598    for(i=0;i<VM_DEVICE_MAX;i++)
599       if (vm->dev_array[i] == dev) {
600          vm->dev_array[i] = NULL;
601          break;
602       }
603 
604    /* Clear device list info */
605    dev->next = NULL;
606    dev->pprev = NULL;
607    return(0);
608 }
609 
610 /* Map a device at the specified physical address */
vm_map_device(vm_instance_t * vm,struct vdevice * dev,m_uint64_t base_addr)611 int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr)
612 {
613 #if 0
614    /* Suspend VM activity */
615    vm_suspend(vm);
616 
617    if (cpu_group_sync_state(vm->cpu_group) == -1) {
618       fprintf(stderr,"VM%u: unable to sync with system CPUs.\n",
619               vm->instance_id);
620       return(-1);
621    }
622 #endif
623 
624    /* Unbind the device if it was already active */
625    vm_unbind_device(vm,dev);
626 
627    /* Map the device at the new base address and rebuild MTS */
628    dev->phys_addr = base_addr;
629    vm_bind_device(vm,dev);
630    cpu_group_rebuild_mts(vm->cpu_group);
631 
632 #if 0
633    vm_resume(vm);
634 #endif
635    return(0);
636 }
637 
638 /* Suspend a VM instance */
vm_suspend(vm_instance_t * vm)639 int vm_suspend(vm_instance_t *vm)
640 {
641    if (vm->status == VM_STATUS_RUNNING) {
642       cpu_group_save_state(vm->cpu_group);
643       cpu_group_set_state(vm->cpu_group,CPU_STATE_SUSPENDED);
644       vm->status = VM_STATUS_SUSPENDED;
645    }
646    return(0);
647 }
648 
649 /* Resume a VM instance */
vm_resume(vm_instance_t * vm)650 int vm_resume(vm_instance_t *vm)
651 {
652    if (vm->status == VM_STATUS_SUSPENDED) {
653       cpu_group_restore_state(vm->cpu_group);
654       vm->status = VM_STATUS_RUNNING;
655    }
656    return(0);
657 }
658 
659 /* Stop an instance */
vm_stop(vm_instance_t * vm)660 int vm_stop(vm_instance_t *vm)
661 {
662    cpu_group_stop_all_cpu(vm->cpu_group);
663    vm->status = VM_STATUS_SHUTDOWN;
664    return(0);
665 }
666 
667 /* Monitor an instance periodically */
vm_monitor(vm_instance_t * vm)668 void vm_monitor(vm_instance_t *vm)
669 {
670    while(vm->status != VM_STATUS_SHUTDOWN)
671       usleep(200000);
672 }
673 
674 /* Create a new chunk */
vm_chunk_create(vm_instance_t * vm)675 static vm_chunk_t *vm_chunk_create(vm_instance_t *vm)
676 {
677    vm_chunk_t *chunk;
678    size_t area_len;
679 
680    if (!(chunk = malloc(sizeof(*chunk))))
681       return NULL;
682 
683    area_len = VM_CHUNK_AREA_SIZE * VM_PAGE_SIZE;
684 
685    if (!(chunk->area = m_memalign(VM_PAGE_SIZE,area_len))) {
686       free(chunk);
687       return NULL;
688    }
689 
690    chunk->page_alloc = 0;
691    chunk->page_total = VM_CHUNK_AREA_SIZE;
692 
693    chunk->next = vm->chunks;
694    vm->chunks = chunk;
695    return chunk;
696 }
697 
698 /* Free a chunk */
vm_chunk_free(vm_chunk_t * chunk)699 static void vm_chunk_free(vm_chunk_t *chunk)
700 {
701    free(chunk->area);
702    free(chunk);
703 }
704 
705 /* Free all chunks used by a VM */
vm_chunk_free_all(vm_instance_t * vm)706 static void vm_chunk_free_all(vm_instance_t *vm)
707 {
708    vm_chunk_t *chunk,*next;
709 
710    for(chunk=vm->chunks;chunk;chunk=next) {
711       next = chunk->next;
712       vm_chunk_free(chunk);
713    }
714 
715    vm->chunks = NULL;
716 }
717 
718 /* Allocate an host page */
vm_alloc_host_page(vm_instance_t * vm)719 void *vm_alloc_host_page(vm_instance_t *vm)
720 {
721    vm_chunk_t *chunk = vm->chunks;
722    void *ptr;
723 
724    if (!chunk || (chunk->page_alloc == chunk->page_total)) {
725       chunk = vm_chunk_create(vm);
726       if (!chunk) return NULL;
727    }
728 
729    ptr = chunk->area + (chunk->page_alloc * VM_PAGE_SIZE);
730    chunk->page_alloc++;
731    return(ptr);
732 }
733 
734 /* Free resources used by a ghost image */
vm_ghost_image_free(vm_ghost_image_t * img)735 static void vm_ghost_image_free(vm_ghost_image_t *img)
736 {
737    if (img) {
738       if (img->fd != -1) {
739          close(img->fd);
740 
741          if (img->area_ptr != NULL)
742             memzone_unmap(img->area_ptr,img->file_size);
743       }
744 
745       free(img->filename);
746       free(img);
747    }
748 }
749 
750 /* Find a specified ghost image in the pool */
vm_ghost_image_find(char * filename)751 static vm_ghost_image_t *vm_ghost_image_find(char *filename)
752 {
753    vm_ghost_image_t *img;
754 
755    for(img=vm_ghost_pool;img;img=img->next)
756       if (!strcmp(img->filename,filename))
757          return img;
758 
759    return NULL;
760 }
761 
762 /* Load a new ghost image */
vm_ghost_image_load(char * filename)763 static vm_ghost_image_t *vm_ghost_image_load(char *filename)
764 {
765    vm_ghost_image_t *img;
766 
767    if (!(img = calloc(1,sizeof(*img))))
768       return NULL;
769 
770    img->fd = -1;
771 
772    if (!(img->filename = strdup(filename))) {
773       vm_ghost_image_free(img);
774       return NULL;
775    }
776 
777    img->fd = memzone_open_file_ro(img->filename,&img->area_ptr,&img->file_size);
778 
779    if (img->fd == -1) {
780       vm_ghost_image_free(img);
781       return NULL;
782    }
783 
784    m_log("GHOST","loaded ghost image %s (fd=%d) at addr=%p (size=0x%llx)\n",
785          img->filename,img->fd,img->area_ptr,(long long)img->file_size);
786 
787    return img;
788 }
789 
790 /* Get a ghost image */
vm_ghost_image_get(char * filename,u_char ** ptr,int * fd)791 int vm_ghost_image_get(char *filename,u_char **ptr,int *fd)
792 {
793    vm_ghost_image_t *img;
794 
795    VM_GLOCK();
796 
797    /* Do we already have this image in the pool ? */
798    if ((img = vm_ghost_image_find(filename)) != NULL) {
799       img->ref_count++;
800       *ptr = img->area_ptr;
801       *fd  = img->fd;
802       VM_GUNLOCK();
803       return(0);
804    }
805 
806    /* Load the ghost file and add it into the pool */
807    if (!(img = vm_ghost_image_load(filename))) {
808       VM_GUNLOCK();
809       fprintf(stderr,"Unable to load ghost image %s\n",filename);
810       return(-1);
811    }
812 
813    img->ref_count = 1;
814    *ptr = img->area_ptr;
815    *fd  = img->fd;
816 
817    img->next = vm_ghost_pool;
818    vm_ghost_pool = img;
819    VM_GUNLOCK();
820 
821    m_log("GHOST","loaded image %s successfully.\n",filename);
822    return(0);
823 }
824 
825 /* Release a ghost image */
vm_ghost_image_release(int fd)826 int vm_ghost_image_release(int fd)
827 {
828    vm_ghost_image_t **img,*next;
829 
830    VM_GLOCK();
831 
832    for(img=&vm_ghost_pool;*img;img=&(*img)->next) {
833       if ((*img)->fd == fd) {
834          assert((*img)->ref_count > 0);
835 
836          (*img)->ref_count--;
837 
838          if ((*img)->ref_count == 0) {
839             m_log("GHOST","unloaded ghost image %s (fd=%d) at "
840                   "addr=%p (size=0x%llx)\n",
841                   (*img)->filename,(*img)->fd,(*img)->area_ptr,
842                   (long long)(*img)->file_size);
843 
844             next = (*img)->next;
845             vm_ghost_image_free(*img);
846             *img = next;
847          }
848 
849          VM_GUNLOCK();
850          return(0);
851       }
852    }
853 
854    VM_GUNLOCK();
855    return(-1);
856 }
857 
858 /* Open a VM file and map it in memory */
vm_mmap_open_file(vm_instance_t * vm,char * name,u_char ** ptr,off_t * fsize)859 int vm_mmap_open_file(vm_instance_t *vm,char *name,
860                       u_char **ptr,off_t *fsize)
861 {
862    char *filename;
863    int fd;
864 
865    if (!(filename = vm_build_filename(vm,name))) {
866       fprintf(stderr,"vm_mmap_open_file: unable to create filename (%s)\n",
867               name);
868       return(-1);
869    }
870 
871    if ((fd = memzone_open_file(filename,ptr,fsize)) == -1)
872       fprintf(stderr,"vm_mmap_open_file: unable to open file '%s' (%s)\n",
873               filename,strerror(errno));
874 
875    free(filename);
876    return(fd);
877 }
878 
879 /* Open/Create a VM file and map it in memory */
vm_mmap_create_file(vm_instance_t * vm,char * name,size_t len,u_char ** ptr)880 int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr)
881 {
882    char *filename;
883    int fd;
884 
885    if (!(filename = vm_build_filename(vm,name))) {
886       fprintf(stderr,"vm_mmap_create_file: unable to create filename (%s)\n",
887               name);
888       return(-1);
889    }
890 
891    if ((fd = memzone_create_file(filename,len,ptr)) == -1)
892       fprintf(stderr,"vm_mmap_create_file: unable to open file '%s' (%s)\n",
893               filename,strerror(errno));
894 
895    free(filename);
896    return(fd);
897 }
898 
899 /* Close a memory mapped file */
vm_mmap_close_file(int fd,u_char * ptr,size_t len)900 int vm_mmap_close_file(int fd,u_char *ptr,size_t len)
901 {
902    if (ptr != NULL)
903       memzone_unmap(ptr,len);
904 
905    if (fd != -1)
906       close(fd);
907 
908    return(0);
909 }
910 
911 /* Save the Cisco IOS configuration from NVRAM */
vm_ios_save_config(vm_instance_t * vm)912 int vm_ios_save_config(vm_instance_t *vm)
913 {
914    char *output;
915    int res;
916 
917    if (!(output = vm_build_filename(vm,"ios_cfg.txt")))
918       return(-1);
919 
920    res = vm_nvram_extract_config(vm,output);
921    free(output);
922    return(res);
923 }
924 
925 /* Set Cisco IOS image to use */
vm_ios_set_image(vm_instance_t * vm,char * ios_image)926 int vm_ios_set_image(vm_instance_t *vm,char *ios_image)
927 {
928    char *str;
929 
930    if (!(str = strdup(ios_image)))
931       return(-1);
932 
933    if (vm->ios_image != NULL) {
934       free(vm->ios_image);
935       vm->ios_image = NULL;
936    }
937 
938    vm->ios_image = str;
939    return(0);
940 }
941 
942 /* Unset a Cisco IOS configuration file */
vm_ios_unset_config(vm_instance_t * vm)943 void vm_ios_unset_config(vm_instance_t *vm)
944 {
945    free(vm->ios_startup_config);
946    vm->ios_startup_config = NULL;
947 
948    free(vm->ios_private_config);
949    vm->ios_private_config = NULL;
950 }
951 
952 /* Set Cisco IOS configuration files to use (NULL to keep existing data) */
vm_ios_set_config(vm_instance_t * vm,const char * startup_filename,const char * private_filename)953 int vm_ios_set_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename)
954 {
955    char *startup_file = NULL;
956    char *private_file = NULL;
957 
958    if (startup_filename) {
959       startup_file = strdup(startup_filename);
960       if (startup_file == NULL)
961          goto err_memory;
962    }
963 
964    if (private_filename) {
965       private_file = strdup(private_filename);
966       if (private_file == NULL)
967          goto err_memory;
968    }
969 
970    vm_ios_unset_config(vm);
971    vm->ios_startup_config = startup_file;
972    vm->ios_private_config = private_file;
973    return(0);
974 err_memory:
975    free(startup_file);
976    free(private_file);
977    return(-1);
978 }
979 
980 /* Extract IOS configuration from NVRAM and write it to a file */
vm_nvram_extract_config(vm_instance_t * vm,char * filename)981 int vm_nvram_extract_config(vm_instance_t *vm,char *filename)
982 {
983    u_char *cfg_buffer = NULL;
984    size_t cfg_len;
985    FILE *fd;
986 
987    if (!vm->platform->nvram_extract_config)
988       return(-1);
989 
990    /* Extract the IOS configuration */
991    if ((vm->platform->nvram_extract_config(vm,&cfg_buffer,&cfg_len,NULL,NULL)) ||
992        (cfg_buffer == NULL))
993       return(-1);
994 
995    /* Write configuration to the specified filename */
996    if (!(fd = fopen(filename,"w"))) {
997       vm_error(vm,"unable to create file '%s'\n",filename);
998       free(cfg_buffer);
999       return(-1);
1000    }
1001 
1002    fwrite(cfg_buffer,cfg_len,1,fd);
1003 
1004    fclose(fd);
1005    free(cfg_buffer);
1006    return(0);
1007 }
1008 
1009 /* Read IOS configuraton from the files and push it to NVRAM (NULL to keep existing data) */
vm_nvram_push_config(vm_instance_t * vm,const char * startup_filename,const char * private_filename)1010 int vm_nvram_push_config(vm_instance_t *vm,const char *startup_filename,const char *private_filename)
1011 {
1012    u_char *startup_config = NULL;
1013    u_char *private_config = NULL;
1014    size_t startup_len = 0;
1015    size_t private_len = 0;
1016    int res = -1;
1017 
1018    /* Read configuration */
1019    if (startup_filename) {
1020       if (m_read_file(startup_filename, &startup_config, &startup_len))
1021          goto cleanup;
1022    }
1023 
1024    if (private_filename) {
1025       if (m_read_file(private_filename, &private_config, &private_len))
1026          goto cleanup;
1027    }
1028 
1029    /* Push it! */
1030    res = vm->platform->nvram_push_config(vm, startup_config, startup_len, private_config, private_len);
1031 
1032 cleanup:
1033    free(startup_config);
1034    free(private_config);
1035    return(res);
1036 }
1037 
1038 /* Save general VM configuration into the specified file */
vm_save_config(vm_instance_t * vm,FILE * fd)1039 void vm_save_config(vm_instance_t *vm,FILE *fd)
1040 {
1041    fprintf(fd,"vm create %s %u %s\n",
1042            vm->name,vm->instance_id,vm->platform->name);
1043 
1044    if (vm->ios_image)
1045       fprintf(fd,"vm set_ios %s %s\n",vm->name,vm->ios_image);
1046 
1047    fprintf(fd,"vm set_ram %s %u\n",vm->name,vm->ram_size);
1048    fprintf(fd,"vm set_nvram %s %u\n",vm->name,vm->nvram_size);
1049    fprintf(fd,"vm set_ram_mmap %s %u\n",vm->name,vm->ram_mmap);
1050    fprintf(fd,"vm set_clock_divisor %s %u\n",vm->name,vm->clock_divisor);
1051    fprintf(fd,"vm set_conf_reg %s 0x%4.4x\n",vm->name,vm->conf_reg_setup);
1052 
1053    if (vm->vtty_con_type == VTTY_TYPE_TCP)
1054       fprintf(fd,"vm set_con_tcp_port %s %d\n",
1055               vm->name,vm->vtty_con_tcp_port);
1056 
1057    if (vm->vtty_aux_type == VTTY_TYPE_TCP)
1058       fprintf(fd,"vm set_aux_tcp_port %s %d\n",
1059               vm->name,vm->vtty_aux_tcp_port);
1060 
1061    /* Save slot config */
1062    vm_slot_save_all_config(vm,fd);
1063 }
1064 
1065 /* Find a platform */
vm_platform_find(char * name)1066 vm_platform_t *vm_platform_find(char *name)
1067 {
1068    struct vm_platform_list *p;
1069 
1070    for(p=vm_platforms;p;p=p->next)
1071       if (!strcmp(p->platform->name,name))
1072          return(p->platform);
1073 
1074    return NULL;
1075 }
1076 
1077 /* Find a platform given its CLI name */
vm_platform_find_cli_name(char * name)1078 vm_platform_t *vm_platform_find_cli_name(char *name)
1079 {
1080    struct vm_platform_list *p;
1081 
1082    for(p=vm_platforms;p;p=p->next)
1083       if (!strcmp(p->platform->cli_name,name))
1084          return(p->platform);
1085 
1086    return NULL;
1087 }
1088 
1089 /* Destroy vm_platforms */
destroy_vm_platforms(void)1090 static void destroy_vm_platforms(void)
1091 {
1092    struct vm_platform_list *p, *next;
1093 
1094    for (p = vm_platforms; p ;p = next) {
1095       next = p->next;
1096       free(p);
1097    }
1098    vm_platforms = NULL;
1099 }
1100 
1101 /* Register a platform */
vm_platform_register(vm_platform_t * platform)1102 int vm_platform_register(vm_platform_t *platform)
1103 {
1104    struct vm_platform_list *p;
1105 
1106    if (vm_platform_find(platform->name) != NULL) {
1107       fprintf(stderr,"vm_platform_register: platform '%s' already exists.\n",
1108               platform->name);
1109       return(-1);
1110    }
1111 
1112    if (!(p = malloc(sizeof(*p)))) {
1113       fprintf(stderr,"vm_platform_register: unable to record platform.\n");
1114       return(-1);
1115    }
1116 
1117    if (!vm_platforms) {
1118       atexit(destroy_vm_platforms);
1119    }
1120 
1121    p->platform = platform;
1122    p->next = vm_platforms;
1123    vm_platforms = p;
1124    return(0);
1125 }
1126 
1127 /* Create an instance of the specified type */
vm_create_instance(char * name,int instance_id,char * type)1128 vm_instance_t *vm_create_instance(char *name,int instance_id,char *type)
1129 {
1130    vm_platform_t *platform;
1131    vm_instance_t *vm = NULL;
1132 
1133    if (!(platform = vm_platform_find(type))) {
1134       fprintf(stderr,"VM %s: unknown platform '%s'\n",name,type);
1135       goto error;
1136    }
1137 
1138    /* Create a generic VM instance */
1139    if (!(vm = vm_create(name,instance_id,platform)))
1140       goto error;
1141 
1142    /* Initialize specific parts */
1143    if (vm->platform->create_instance(vm) == -1)
1144       goto error;
1145 
1146    return vm;
1147 
1148  error:
1149    fprintf(stderr,"VM %s: unable to create instance!\n",name);
1150    vm_free(vm);
1151    return NULL;
1152 }
1153 
1154 /* Free resources used by a VM instance */
vm_reg_delete_instance(void * data,void * arg)1155 static int vm_reg_delete_instance(void *data,void *arg)
1156 {
1157    vm_instance_t *vm = data;
1158    return(vm->platform->delete_instance(vm));
1159 }
1160 
1161 /* Delete a VM instance */
vm_delete_instance(char * name)1162 int vm_delete_instance(char *name)
1163 {
1164    return(registry_delete_if_unused(name,OBJ_TYPE_VM,
1165                                     vm_reg_delete_instance,NULL));
1166 }
1167 
1168 /* Rename a VM instance */
vm_rename_instance(vm_instance_t * vm,char * name)1169 int vm_rename_instance(vm_instance_t *vm, char *name)
1170 {
1171    char *old_name;
1172    char *old_lock_file = NULL;
1173    FILE *old_lock_fd = NULL;
1174    glob_t globbuf;
1175    size_t i;
1176    char *pattern = NULL;
1177    char *filename;
1178    int do_rename = 0;
1179 
1180    if (name == NULL || vm == NULL)
1181       goto err_invalid; /* invalid argument */
1182 
1183    if (vm->status != VM_STATUS_HALTED)
1184       goto err_not_stopped; /* VM is not stopped */
1185 
1186    if (strcmp(vm->name, name) == 0)
1187       return(0); /* same name, done */
1188 
1189    if (registry_exists(name,OBJ_TYPE_VM))
1190       goto err_exists; /* name already exists */
1191 
1192    old_name = vm->name;
1193    vm->name = NULL;
1194 
1195    if(!(vm->name = strdup(name)))
1196       goto err_strdup; /* out of memory */
1197 
1198    /* get new lock */
1199    do_rename = ( vm_file_naming_type != 1 );
1200    if (do_rename) {
1201       old_lock_file = vm->lock_file;
1202       old_lock_fd = vm->lock_fd;
1203       vm->lock_file = NULL;
1204       vm->lock_fd = NULL;
1205 
1206       if (vm_get_lock(vm) == -1)
1207          goto err_lock;
1208    }
1209 
1210    if (registry_rename(old_name,vm->name,OBJ_TYPE_VM))
1211       goto err_registry; /* failed to rename */
1212 
1213    vm_log(vm,"VM","renamed from '%s' to '%s'",old_name,vm->name);
1214 
1215    /* rename files (best effort) */
1216    if (do_rename) {
1217       fclose(old_lock_fd);
1218       unlink(old_lock_file);
1219       free(old_lock_file);
1220 
1221       vm_close_log(vm);
1222 
1223       if ((pattern = dyn_sprintf("%s_%s_*",vm_get_type(vm),old_name)) == NULL)
1224          goto skip_rename;
1225 
1226       if (glob(pattern, GLOB_NOSORT, NULL, &globbuf) != 0)
1227          goto skip_rename;
1228 
1229       for (i = 0; i < globbuf.gl_pathc; i++) {
1230          if ((filename = dyn_sprintf("%s_%s_%s",vm_get_type(vm),vm->name,globbuf.gl_pathv[i] + strlen(pattern) - 1)) == NULL)
1231             break; /* out of memory */
1232 
1233          rename(globbuf.gl_pathv[i], filename);
1234          free(filename);
1235       }
1236       globfree(&globbuf);
1237  skip_rename:
1238       free(pattern);
1239 
1240       vm_reopen_log(vm);
1241    }
1242 
1243    free(old_name);
1244    return(0); // done
1245 
1246  err_registry:
1247  err_lock:
1248  err_strdup:
1249    free(vm->name);
1250    vm->name = old_name;
1251 
1252    if (do_rename) {
1253       vm_release_lock(vm,TRUE);
1254       vm->lock_file = old_lock_file;
1255       vm->lock_fd = old_lock_fd;
1256    }
1257  err_exists:
1258  err_not_stopped:
1259  err_invalid:
1260    return(-1);
1261 }
1262 
1263 /* Initialize a VM instance */
vm_init_instance(vm_instance_t * vm)1264 int vm_init_instance(vm_instance_t *vm)
1265 {
1266    return(vm->platform->init_instance(vm));
1267 }
1268 
1269 /* Stop a VM instance */
vm_stop_instance(vm_instance_t * vm)1270 int vm_stop_instance(vm_instance_t *vm)
1271 {
1272    return(vm->platform->stop_instance(vm));
1273 }
1274 
1275 /* Delete all VM instances */
vm_delete_all_instances(void)1276 int vm_delete_all_instances(void)
1277 {
1278    return(registry_delete_type(OBJ_TYPE_VM,vm_reg_delete_instance,NULL));
1279 }
1280 
1281 /* Save configurations of all VM instances */
vm_reg_save_config(registry_entry_t * entry,void * opt,int * err)1282 static void vm_reg_save_config(registry_entry_t *entry,void *opt,int *err)
1283 {
1284    vm_instance_t *vm = entry->data;
1285    FILE *fd = opt;
1286 
1287    vm_save_config(vm,fd);
1288 
1289    /* Save specific platform options */
1290    if (vm->platform->save_config != NULL)
1291       vm->platform->save_config(vm,fd);
1292 }
1293 
1294 /* Save all VM configs */
vm_save_config_all(FILE * fd)1295 int vm_save_config_all(FILE *fd)
1296 {
1297    registry_foreach_type(OBJ_TYPE_VM,vm_reg_save_config,fd,NULL);
1298    return(0);
1299 }
1300 
1301 /* OIR to start a slot/subslot */
vm_oir_start(vm_instance_t * vm,u_int slot,u_int subslot)1302 int vm_oir_start(vm_instance_t *vm,u_int slot,u_int subslot)
1303 {
1304    if (vm->platform->oir_start != NULL)
1305       return(vm->platform->oir_start(vm,slot,subslot));
1306 
1307    /* OIR not supported */
1308    return(-1);
1309 }
1310 
1311 /* OIR to stop a slot/subslot */
vm_oir_stop(vm_instance_t * vm,u_int slot,u_int subslot)1312 int vm_oir_stop(vm_instance_t *vm,u_int slot,u_int subslot)
1313 {
1314    if (vm->platform->oir_stop != NULL)
1315       return(vm->platform->oir_stop(vm,slot,subslot));
1316 
1317    /* OIR not supported */
1318    return(-1);
1319 }
1320 
1321 /* Set the JIT translation sharing group */
vm_set_tsg(vm_instance_t * vm,int group)1322 int vm_set_tsg(vm_instance_t *vm,int group)
1323 {
1324    if (vm->status == VM_STATUS_RUNNING)
1325       return(-1);
1326 
1327    vm->tsg = group;
1328    return(0);
1329 }
1330 
1331 
1332 
1333