/[dynamips]/upstream/dynamips-0.2.5/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

Contents of /upstream/dynamips-0.2.5/vm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Sat Oct 6 16:01:44 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 16265 byte(s)
import 0.2.5 from upstream

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