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