/[dynamips]/trunk/vm.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/vm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (hide annotations)
Sat Oct 6 16:05:34 2007 UTC (11 years, 10 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.6-RC2/vm.c
File MIME type: text/plain
File size: 16265 byte(s)
dynamips-0.2.6-RC2

1 dpavlin 1 /*
2     * Cisco 7200 (Predator) simulation platform.
3     * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4     *
5     * Virtual machine abstraction.
6     *
7     * TODO: IRQ Routing.
8     */
9    
10     #include <stdio.h>
11     #include <stdlib.h>
12     #include <string.h>
13     #include <unistd.h>
14     #include <fcntl.h>
15     #include <sys/types.h>
16     #include <assert.h>
17    
18     #include "registry.h"
19     #include "device.h"
20     #include "pci_dev.h"
21     #include "pci_io.h"
22     #include "vm.h"
23     #include "dev_vtty.h"
24    
25     #include ARCH_INC_FILE
26    
27     #define DEBUG_VM 1
28    
29     /* Type of VM file naming (0=use VM name, 1=use instance ID) */
30     int vm_file_naming_type = 0;
31    
32     /* Initialize a VM object */
33     void vm_object_init(vm_obj_t *obj)
34     {
35     memset(obj,0,sizeof(*obj));
36     }
37    
38     /* Add a VM object to an instance */
39     void vm_object_add(vm_instance_t *vm,vm_obj_t *obj)
40     {
41     obj->next = vm->vm_object_list;
42     obj->pprev = &vm->vm_object_list;
43    
44     if (vm->vm_object_list)
45     vm->vm_object_list->pprev = &obj->next;
46    
47     vm->vm_object_list = obj;
48     }
49    
50     /* Remove a VM object from an instance */
51     void vm_object_remove(vm_instance_t *vm,vm_obj_t *obj)
52     {
53     if (obj->next)
54     obj->next->pprev = obj->pprev;
55     *(obj->pprev) = obj->next;
56     }
57    
58     /* Find an object given its name */
59     vm_obj_t *vm_object_find(vm_instance_t *vm,char *name)
60     {
61     vm_obj_t *obj;
62    
63     for(obj=vm->vm_object_list;obj;obj=obj->next)
64     if (!strcmp(obj->name,name))
65     return obj;
66    
67     return NULL;
68     }
69    
70     /* Check that a mandatory object is present */
71     int vm_object_check(vm_instance_t *vm,char *name)
72     {
73     return(vm_object_find(vm,name) ? 0 : -1);
74     }
75    
76     /* Shut down all objects of an instance */
77     void vm_object_free_list(vm_instance_t *vm)
78     {
79     vm_obj_t *obj,*next;
80    
81     for(obj=vm->vm_object_list;obj;obj=next) {
82     next = obj->next;
83    
84     if (obj->shutdown != NULL) {
85     #if DEBUG_VM
86     vm_log(vm,"VM_OBJECT","Shutdown of object \"%s\"\n",obj->name);
87     #endif
88     obj->shutdown(vm,obj->data);
89     }
90     }
91    
92     vm->vm_object_list = NULL;
93     }
94    
95     /* Dump the object list of an instance */
96     void vm_object_dump(vm_instance_t *vm)
97     {
98     vm_obj_t *obj;
99    
100     printf("VM \"%s\" (%u) object list:\n",vm->name,vm->instance_id);
101    
102     for(obj=vm->vm_object_list;obj;obj=obj->next) {
103     printf(" - %-15s [data=%p]\n",obj->name,obj->data);
104     }
105    
106     printf("\n");
107     }
108    
109     /* Get VM type */
110     char *vm_get_type(vm_instance_t *vm)
111     {
112     char *machine;
113    
114     switch(vm->type) {
115     case VM_TYPE_C3600:
116     machine = "c3600";
117     break;
118     case VM_TYPE_C7200:
119     machine = "c7200";
120     break;
121     default:
122     machine = "unknown";
123     break;
124     }
125    
126     return machine;
127     }
128    
129     /* Get platform type */
130     char *vm_get_platform_type(vm_instance_t *vm)
131     {
132     char *machine;
133    
134     switch(vm->type) {
135     case VM_TYPE_C3600:
136     machine = "C3600";
137     break;
138     case VM_TYPE_C7200:
139     machine = "C7200";
140     break;
141     default:
142     machine = "VM";
143     break;
144     }
145    
146     return machine;
147     }
148    
149     /* Generate a filename for use by the instance */
150     char *vm_build_filename(vm_instance_t *vm,char *name)
151     {
152     char *filename,*machine;
153    
154     machine = vm_get_type(vm);
155    
156     switch(vm_file_naming_type) {
157     case 1:
158     filename = dyn_sprintf("%s_i%u_%s",machine,vm->instance_id,name);
159     break;
160     case 0:
161     default:
162     filename = dyn_sprintf("%s_%s_%s",machine,vm->name,name);
163     break;
164     }
165    
166     assert(filename != NULL);
167     return filename;
168     }
169    
170     /* Erase lock file */
171     void vm_release_lock(vm_instance_t *vm,int erase)
172     {
173     if (vm->lock_fd != NULL) {
174     fclose(vm->lock_fd);
175     vm->lock_fd = NULL;
176     }
177    
178     if (vm->lock_file != NULL) {
179     if (erase)
180     unlink(vm->lock_file);
181     free(vm->lock_file);
182     vm->lock_file = NULL;
183     }
184     }
185    
186     /* Check that an instance lock file doesn't already exist */
187     int vm_get_lock(vm_instance_t *vm)
188     {
189     char pid_str[32];
190     struct flock lock;
191    
192     vm->lock_file = vm_build_filename(vm,"lock");
193    
194     if (!(vm->lock_fd = fopen(vm->lock_file,"w"))) {
195     fprintf(stderr,"Unable to create lock file \"%s\".\n",vm->lock_file);
196     return(-1);
197     }
198    
199     memset(&lock,0,sizeof(lock));
200     lock.l_type = F_WRLCK;
201     lock.l_whence = SEEK_SET;
202     lock.l_start = 0;
203     lock.l_len = 0;
204    
205     if (fcntl(fileno(vm->lock_fd),F_SETLK,&lock) == -1) {
206     if (fcntl(fileno(vm->lock_fd),F_GETLK,&lock) == 0) {
207     snprintf(pid_str,sizeof(pid_str),"%ld",(long)lock.l_pid);
208     } else {
209     strcpy(pid_str,"unknown");
210     }
211    
212     fprintf(stderr,
213     "\nAn emulator instance (PID %s) is already running with "
214     "identifier %u.\n"
215     "If this is not the case, please erase file \"%s\".\n\n",
216     pid_str,vm->instance_id,vm->lock_file);
217     vm_release_lock(vm,FALSE);
218     return(-1);
219     }
220    
221     /* write the emulator PID */
222     fprintf(vm->lock_fd,"%ld\n",(u_long)getpid());
223     return(0);
224     }
225    
226     /* Log a message */
227     void vm_flog(vm_instance_t *vm,char *module,char *format,va_list ap)
228     {
229     if (vm->log_fd)
230     m_flog(vm->log_fd,module,format,ap);
231     }
232    
233     /* Log a message */
234     void vm_log(vm_instance_t *vm,char *module,char *format,...)
235     {
236     va_list ap;
237    
238     va_start(ap,format);
239     vm_flog(vm,module,format,ap);
240     va_end(ap);
241     }
242    
243     /* Close the log file */
244     int vm_close_log(vm_instance_t *vm)
245     {
246     if (vm->log_fd)
247     fclose(vm->log_fd);
248    
249     free(vm->log_file);
250    
251     vm->log_file = NULL;
252     vm->log_fd = NULL;
253     return(0);
254     }
255    
256     /* Create the log file */
257     int vm_create_log(vm_instance_t *vm)
258     {
259     vm_close_log(vm);
260    
261     if (!(vm->log_file = vm_build_filename(vm,"log.txt")))
262     return(-1);
263    
264     if (!(vm->log_fd = fopen(vm->log_file,"w"))) {
265     fprintf(stderr,"VM %s: unable to create log file '%s'\n",
266     vm->name,vm->log_file);
267     free(vm->log_file);
268     vm->log_file = NULL;
269     return(-1);
270     }
271    
272     return(0);
273     }
274    
275     /* Error message */
276     void vm_error(vm_instance_t *vm,char *format,...)
277     {
278     char buffer[2048];
279     va_list ap;
280    
281     va_start(ap,format);
282     vsnprintf(buffer,sizeof(buffer),format,ap);
283     va_end(ap);
284    
285     fprintf(stderr,"%s '%s': %s",vm_get_platform_type(vm),vm->name,buffer);
286     }
287    
288     /* Create a new VM instance */
289     vm_instance_t *vm_create(char *name,int instance_id,int machine_type)
290     {
291     vm_instance_t *vm;
292    
293     if (!(vm = malloc(sizeof(*vm)))) {
294     fprintf(stderr,"VM %s: unable to create new instance!\n",name);
295     return NULL;
296     }
297    
298     memset(vm,0,sizeof(*vm));
299     vm->instance_id = instance_id;
300     vm->type = machine_type;
301     vm->status = VM_STATUS_HALTED;
302     vm->jit_use = JIT_SUPPORT;
303     vm->vtty_con_type = VTTY_TYPE_TERM;
304     vm->vtty_aux_type = VTTY_TYPE_NONE;
305     vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV;
306    
307     if (!(vm->name = strdup(name))) {
308     fprintf(stderr,"VM %s: unable to store instance name!\n",name);
309     goto err_name;
310     }
311    
312     /* create lock file */
313     if (vm_get_lock(vm) == -1)
314     goto err_lock;
315    
316     /* create log file */
317     if (vm_create_log(vm) == -1)
318     goto err_log;
319    
320     if (registry_add(vm->name,OBJ_TYPE_VM,vm) == -1) {
321     fprintf(stderr,"VM: Unable to store instance '%s' in registry!\n",
322     vm->name);
323     goto err_reg_add;
324     }
325    
326     return vm;
327    
328     err_reg_add:
329     vm_close_log(vm);
330     err_log:
331     free(vm->lock_file);
332     err_lock:
333     free(vm->name);
334     err_name:
335     free(vm);
336     return NULL;
337     }
338    
339     /*
340     * Shutdown hardware resources used by a VM.
341     * The CPU must have been stopped.
342     */
343     int vm_hardware_shutdown(vm_instance_t *vm)
344     {
345     int i;
346    
347     if ((vm->status == VM_STATUS_HALTED) || !vm->cpu_group) {
348     vm_log(vm,"VM","trying to shutdown an inactive VM.\n");
349     return(-1);
350     }
351    
352     vm_log(vm,"VM","shutdown procedure engaged.\n");
353    
354     /* Mark the VM as halted */
355     vm->status = VM_STATUS_HALTED;
356    
357     /* Disable NVRAM operations */
358     vm->nvram_extract_config = NULL;
359     vm->nvram_push_config = NULL;
360    
361     /* Free the object list */
362     vm_object_free_list(vm);
363    
364     /* Free resources used by PCI busses */
365     vm_log(vm,"VM","removing PCI busses.\n");
366     pci_io_data_remove(vm,vm->pci_io_space);
367     pci_bus_remove(vm->pci_bus[0]);
368     pci_bus_remove(vm->pci_bus[1]);
369     vm->pci_bus[0] = vm->pci_bus[1] = NULL;
370    
371     /* Free the PCI bus pool */
372     for(i=0;i<VM_PCI_POOL_SIZE;i++) {
373     if (vm->pci_bus_pool[i] != NULL) {
374     pci_bus_remove(vm->pci_bus_pool[i]);
375     vm->pci_bus_pool[i] = NULL;
376     }
377     }
378    
379     /* Delete the VTTY for Console and AUX ports */
380     vm_log(vm,"VM","deleting VTTY.\n");
381     vm_delete_vtty(vm);
382    
383     /* Delete system CPU group */
384     vm_log(vm,"VM","deleting system CPUs.\n");
385     cpu_group_delete(vm->cpu_group);
386     vm->cpu_group = NULL;
387     vm->boot_cpu = NULL;
388    
389     vm_log(vm,"VM","shutdown procedure completed.\n");
390     return(0);
391     }
392    
393     /* Free resources used by a VM */
394     void vm_free(vm_instance_t *vm)
395     {
396     if (vm != NULL) {
397     /* Free hardware resources */
398     vm_hardware_shutdown(vm);
399    
400     /* Close log file */
401     vm_close_log(vm);
402    
403     /* Remove the lock file */
404     vm_release_lock(vm,TRUE);
405    
406     /* Free various elements */
407     free(vm->sym_filename);
408     free(vm->ios_image);
409     free(vm->ios_config);
410     free(vm->rom_filename);
411     free(vm->name);
412     free(vm);
413     }
414     }
415    
416     /* Get an instance given a name */
417     vm_instance_t *vm_acquire(char *name)
418     {
419     return(registry_find(name,OBJ_TYPE_VM));
420     }
421    
422     /* Release a VM (decrement reference count) */
423     int vm_release(vm_instance_t *vm)
424     {
425     return(registry_unref(vm->name,OBJ_TYPE_VM));
426     }
427    
428     /* Initialize VTTY */
429     int vm_init_vtty(vm_instance_t *vm)
430     {
431     /* Create Console and AUX ports */
432     vm->vtty_con = vtty_create(vm,"Console port",
433     vm->vtty_con_type,vm->vtty_con_tcp_port,
434     &vm->vtty_con_serial_option);
435    
436     vm->vtty_aux = vtty_create(vm,"AUX port",
437     vm->vtty_aux_type,vm->vtty_aux_tcp_port,
438     &vm->vtty_aux_serial_option);
439     return(0);
440     }
441    
442     /* Delete VTTY */
443     void vm_delete_vtty(vm_instance_t *vm)
444     {
445     vtty_delete(vm->vtty_con);
446     vtty_delete(vm->vtty_aux);
447     vm->vtty_con = vm->vtty_aux = NULL;
448     }
449    
450     /* Bind a device to a virtual machine */
451     int vm_bind_device(vm_instance_t *vm,struct vdevice *dev)
452     {
453     struct vdevice **cur;
454     u_int i;
455    
456     /*
457     * Add this device to the device array. The index in the device array
458     * is used by the MTS subsystem.
459     */
460     for(i=0;i<MIPS64_DEVICE_MAX;i++)
461     if (!vm->dev_array[i])
462     break;
463    
464     if (i == MIPS64_DEVICE_MAX) {
465     fprintf(stderr,"VM%u: vm_bind_device: device table full.\n",
466     vm->instance_id);
467     return(-1);
468     }
469    
470     vm->dev_array[i] = dev;
471     dev->id = i;
472    
473     /*
474     * Add it to the linked-list (devices are ordered by physical addresses).
475     */
476     for(cur=&vm->dev_list;*cur;cur=&(*cur)->next)
477     if ((*cur)->phys_addr > dev->phys_addr)
478     break;
479    
480     dev->next = *cur;
481     if (*cur) (*cur)->pprev = &dev->next;
482     dev->pprev = cur;
483     *cur = dev;
484     return(0);
485     }
486    
487     /* Unbind a device from a virtual machine */
488     int vm_unbind_device(vm_instance_t *vm,struct vdevice *dev)
489     {
490     u_int i;
491    
492     if (!dev->pprev)
493     return(-1);
494    
495     /* Remove the device from the linked list */
496     if (dev->next)
497     dev->next->pprev = dev->pprev;
498    
499     *(dev->pprev) = dev->next;
500    
501     /* Remove the device from the device array */
502     for(i=0;i<MIPS64_DEVICE_MAX;i++)
503     if (vm->dev_array[i] == dev) {
504     vm->dev_array[i] = NULL;
505     break;
506     }
507    
508     /* Clear device list info */
509     dev->next = NULL;
510     dev->pprev = NULL;
511     return(0);
512     }
513    
514     /* Map a device at the specified physical address */
515     int vm_map_device(vm_instance_t *vm,struct vdevice *dev,m_uint64_t base_addr)
516     {
517     #if 0
518     /* Suspend VM activity */
519     vm_suspend(vm);
520    
521     if (cpu_group_sync_state(vm->cpu_group) == -1) {
522     fprintf(stderr,"VM%u: unable to sync with system CPUs.\n",
523     vm->instance_id);
524     return(-1);
525     }
526     #endif
527    
528     /* Unbind the device if it was already active */
529     vm_unbind_device(vm,dev);
530    
531     /* Map the device at the new base address and rebuild MTS */
532     dev->phys_addr = base_addr;
533     vm_bind_device(vm,dev);
534     cpu_group_rebuild_mts(vm->cpu_group);
535    
536     #if 0
537     vm_resume(vm);
538     #endif
539     return(0);
540     }
541    
542     /* Set an IRQ for a VM */
543     void vm_set_irq(vm_instance_t *vm,u_int irq)
544     {
545     if (vm->boot_cpu->irq_disable) {
546     vm->boot_cpu->irq_pending = 0;
547     return;
548     }
549    
550     /* TODO: IRQ routing */
551     mips64_set_irq(vm->boot_cpu,irq);
552    
553     if (vm->boot_cpu->irq_idle_preempt[irq])
554     mips64_idle_break_wait(vm->boot_cpu);
555     }
556    
557     /* Clear an IRQ for a VM */
558     void vm_clear_irq(vm_instance_t *vm,u_int irq)
559     {
560     /* TODO: IRQ routing */
561     mips64_clear_irq(vm->boot_cpu,irq);
562     }
563    
564     /* Suspend a VM instance */
565     int vm_suspend(vm_instance_t *vm)
566     {
567     if (vm->status == VM_STATUS_RUNNING) {
568     cpu_group_save_state(vm->cpu_group);
569     cpu_group_set_state(vm->cpu_group,MIPS_CPU_SUSPENDED);
570     vm->status = VM_STATUS_SUSPENDED;
571     }
572     return(0);
573     }
574    
575     /* Resume a VM instance */
576     int vm_resume(vm_instance_t *vm)
577     {
578     if (vm->status == VM_STATUS_SUSPENDED) {
579     cpu_group_restore_state(vm->cpu_group);
580     vm->status = VM_STATUS_RUNNING;
581     }
582     return(0);
583     }
584    
585     /* Stop an instance */
586     int vm_stop(vm_instance_t *vm)
587     {
588     cpu_group_stop_all_cpu(vm->cpu_group);
589     vm->status = VM_STATUS_SHUTDOWN;
590     return(0);
591     }
592    
593     /* Monitor an instance periodically */
594     void vm_monitor(vm_instance_t *vm)
595     {
596     while(vm->status != VM_STATUS_SHUTDOWN)
597     usleep(200000);
598     }
599    
600     /* Save the Cisco IOS configuration from NVRAM */
601     int vm_ios_save_config(vm_instance_t *vm)
602     {
603     char *output;
604     int res;
605    
606     if (!(output = vm_build_filename(vm,"ios_cfg.txt")))
607     return(-1);
608    
609     res = vm_nvram_extract_config(vm,output);
610     free(output);
611     return(res);
612     }
613    
614     /* Set Cisco IOS image to use */
615     int vm_ios_set_image(vm_instance_t *vm,char *ios_image)
616     {
617     char *str;
618    
619     if (!(str = strdup(ios_image)))
620     return(-1);
621    
622     if (vm->ios_image != NULL) {
623     free(vm->ios_image);
624     vm->ios_image = NULL;
625     }
626    
627     vm->ios_image = str;
628     return(0);
629     }
630    
631     /* Unset a Cisco IOS configuration file */
632     void vm_ios_unset_config(vm_instance_t *vm)
633     {
634     if (vm->ios_config != NULL) {
635     free(vm->ios_config);
636     vm->ios_config = NULL;
637     }
638     }
639    
640     /* Set Cisco IOS configuration file to use */
641     int vm_ios_set_config(vm_instance_t *vm,char *ios_config)
642     {
643     char *str;
644    
645     if (!(str = strdup(ios_config)))
646     return(-1);
647    
648     vm_ios_unset_config(vm);
649     vm->ios_config = str;
650     return(0);
651     }
652    
653     /* Extract IOS configuration from NVRAM and write it to a file */
654     int vm_nvram_extract_config(vm_instance_t *vm,char *filename)
655     {
656     char *cfg_buffer;
657     ssize_t cfg_len;
658     FILE *fd;
659    
660     if (!vm->nvram_extract_config)
661     return(-1);
662    
663     /* Extract the IOS configuration */
664     if (((cfg_len = vm->nvram_extract_config(vm,&cfg_buffer)) < 0) ||
665     (cfg_buffer == NULL))
666     return(-1);
667    
668     /* Write configuration to the specified filename */
669     if (!(fd = fopen(filename,"w"))) {
670     vm_error(vm,"unable to create file '%s'\n",filename);
671     free(cfg_buffer);
672     return(-1);
673     }
674    
675     fwrite(cfg_buffer,cfg_len,1,fd);
676    
677     fclose(fd);
678     free(cfg_buffer);
679     return(0);
680     }
681    
682     /* Read an IOS configuraton from a file and push it to NVRAM */
683     int vm_nvram_push_config(vm_instance_t *vm,char *filename)
684     {
685     char *cfg_buffer;
686     ssize_t len;
687     int res;
688    
689     if (!vm->nvram_push_config)
690     return(-1);
691    
692     /* Read configuration */
693     if (((len = m_read_file(filename,&cfg_buffer)) <= 0) || !cfg_buffer)
694     return(-1);
695    
696     /* Push it! */
697     res = vm->nvram_push_config(vm,cfg_buffer,len);
698     free(cfg_buffer);
699     return(res);
700     }
701    
702     /* Save general VM configuration into the specified file */
703     void vm_save_config(vm_instance_t *vm,FILE *fd)
704     {
705     if (vm->ios_image)
706     fprintf(fd,"vm set_ios %s %s\n",vm->name,vm->ios_image);
707    
708     fprintf(fd,"vm set_ram %s %u\n",vm->name,vm->ram_size);
709     fprintf(fd,"vm set_nvram %s %u\n",vm->name,vm->nvram_size);
710     fprintf(fd,"vm set_ram_mmap %s %u\n",vm->name,vm->ram_mmap);
711     fprintf(fd,"vm set_clock_divisor %s %u\n",vm->name,vm->clock_divisor);
712     fprintf(fd,"vm set_conf_reg %s 0x%4.4x\n",vm->name,vm->conf_reg_setup);
713    
714     if (vm->vtty_con_type == VTTY_TYPE_TCP)
715     fprintf(fd,"vm set_con_tcp_port %s %d\n",vm->name,vm->vtty_con_tcp_port);
716    
717     if (vm->vtty_aux_type == VTTY_TYPE_TCP)
718     fprintf(fd,"vm set_aux_tcp_port %s %d\n",vm->name,vm->vtty_aux_tcp_port);
719     }

  ViewVC Help
Powered by ViewVC 1.1.26