--- upstream/dynamips-0.2.6-RC3/vm.c 2007/10/06 16:06:49 4 +++ trunk/vm.c 2007/10/06 16:45:40 12 @@ -1,10 +1,8 @@ /* - * Cisco 7200 (Predator) simulation platform. + * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual machine abstraction. - * - * TODO: IRQ Routing. */ #include @@ -20,16 +18,33 @@ #include "device.h" #include "pci_dev.h" #include "pci_io.h" +#include "cpu.h" +#include "mips64_jit.h" #include "vm.h" #include "dev_vtty.h" -#include ARCH_INC_FILE +#include MIPS64_ARCH_INC_FILE #define DEBUG_VM 1 +#define VM_GLOCK() pthread_mutex_lock(&vm_global_lock) +#define VM_GUNLOCK() pthread_mutex_unlock(&vm_global_lock) + /* Type of VM file naming (0=use VM name, 1=use instance ID) */ int vm_file_naming_type = 0; +/* Platform list */ +static struct vm_platform_list *vm_platforms = NULL; + +/* Pool of ghost images */ +static vm_ghost_image_t *vm_ghost_pool = NULL; + +/* Global lock for VM manipulation */ +static pthread_mutex_t vm_global_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Free all chunks used by a VM */ +static void vm_chunk_free_all(vm_instance_t *vm); + /* Initialize a VM object */ void vm_object_init(vm_obj_t *obj) { @@ -95,6 +110,15 @@ vm->vm_object_list = NULL; } +/* Rebuild the object list pointers */ +static void vm_object_rebuild_list(vm_instance_t *vm) +{ + vm_obj_t **obj; + + for(obj=&vm->vm_object_list;*obj;obj=&(*obj)->next) + (*obj)->pprev = obj; +} + /* Dump the object list of an instance */ void vm_object_dump(vm_instance_t *vm) { @@ -112,83 +136,27 @@ /* Get VM type */ char *vm_get_type(vm_instance_t *vm) { - char *machine; - - switch(vm->type) { - case VM_TYPE_C3600: - machine = "c3600"; - break; - case VM_TYPE_C7200: - machine = "c7200"; - break; - case VM_TYPE_C2691: - machine = "c2691"; - break; - case VM_TYPE_C3725: - machine = "c3725"; - break; - case VM_TYPE_C3745: - machine = "c3745"; - break; - default: - machine = "unknown"; - break; - } - - return machine; + return vm->platform->name; } -/* Get platform type */ -char *vm_get_platform_type(vm_instance_t *vm) +/* Get log name */ +static char *vm_get_log_name(vm_instance_t *vm) { - char *machine; + if (vm->platform->log_name != NULL) + return vm->platform->log_name; - switch(vm->type) { - case VM_TYPE_C3600: - machine = "C3600"; - break; - case VM_TYPE_C7200: - machine = "C7200"; - break; - case VM_TYPE_C2691: - machine = "C2691"; - break; - case VM_TYPE_C3725: - machine = "C3725"; - break; - case VM_TYPE_C3745: - machine = "C3745"; - break; - default: - machine = "VM"; - break; - } - - return machine; + /* default value */ + return "VM"; } /* Get MAC address MSB */ u_int vm_get_mac_addr_msb(vm_instance_t *vm) { - switch(vm->type) { - case VM_TYPE_C3600: - return(0xCC); - - case VM_TYPE_C7200: - return(0xCA); - - case VM_TYPE_C2691: - return(0xC0); - - case VM_TYPE_C3725: - return(0xC2); - - case VM_TYPE_C3745: - return(0xC4); - - default: - return(0xC6); - } + if (vm->platform->get_mac_addr_msb != NULL) + return(vm->platform->get_mac_addr_msb()); + + /* default value */ + return(0xC6); } /* Generate a filename for use by the instance */ @@ -280,9 +248,11 @@ { va_list ap; - va_start(ap,format); - vm_flog(vm,module,format,ap); - va_end(ap); + if (vm->log_fd) { + va_start(ap,format); + vm_flog(vm,module,format,ap); + va_end(ap); + } } /* Close the log file */ @@ -301,17 +271,19 @@ /* Create the log file */ int vm_create_log(vm_instance_t *vm) { - vm_close_log(vm); + if (vm->log_file_enabled) { + vm_close_log(vm); - if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) - return(-1); + if (!(vm->log_file = vm_build_filename(vm,"log.txt"))) + return(-1); - if (!(vm->log_fd = fopen(vm->log_file,"w"))) { - fprintf(stderr,"VM %s: unable to create log file '%s'\n", - vm->name,vm->log_file); - free(vm->log_file); - vm->log_file = NULL; - return(-1); + if (!(vm->log_fd = fopen(vm->log_file,"w"))) { + fprintf(stderr,"VM %s: unable to create log file '%s'\n", + vm->name,vm->log_file); + free(vm->log_file); + vm->log_file = NULL; + return(-1); + } } return(0); @@ -327,11 +299,12 @@ vsnprintf(buffer,sizeof(buffer),format,ap); va_end(ap); - fprintf(stderr,"%s '%s': %s",vm_get_platform_type(vm),vm->name,buffer); + fprintf(stderr,"%s '%s': %s",vm_get_log_name(vm),vm->name,buffer); } /* Create a new VM instance */ -vm_instance_t *vm_create(char *name,int instance_id,int machine_type) +static vm_instance_t *vm_create(char *name,int instance_id, + vm_platform_t *platform) { vm_instance_t *vm; @@ -341,19 +314,29 @@ } memset(vm,0,sizeof(*vm)); - vm->instance_id = instance_id; - vm->type = machine_type; - vm->status = VM_STATUS_HALTED; - vm->jit_use = JIT_SUPPORT; - vm->vtty_con_type = VTTY_TYPE_TERM; - vm->vtty_aux_type = VTTY_TYPE_NONE; - vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV; if (!(vm->name = strdup(name))) { fprintf(stderr,"VM %s: unable to store instance name!\n",name); goto err_name; } + vm->instance_id = instance_id; + vm->platform = platform; + vm->status = VM_STATUS_HALTED; + vm->jit_use = JIT_SUPPORT; + vm->exec_blk_direct_jump = TRUE; + vm->vtty_con_type = VTTY_TYPE_TERM; + vm->vtty_aux_type = VTTY_TYPE_NONE; + vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV; + vm->log_file_enabled = TRUE; + vm->rommon_vars.filename = vm_build_filename(vm,"rommon_vars"); + + if (!vm->rommon_vars.filename) + goto err_rommon; + + /* XXX */ + rommon_load_file(&vm->rommon_vars); + /* create lock file */ if (vm_get_lock(vm) == -1) goto err_lock; @@ -375,6 +358,8 @@ err_log: free(vm->lock_file); err_lock: + free(vm->rommon_vars.filename); + err_rommon: free(vm->name); err_name: free(vm); @@ -399,10 +384,6 @@ /* Mark the VM as halted */ vm->status = VM_STATUS_HALTED; - /* Disable NVRAM operations */ - vm->nvram_extract_config = NULL; - vm->nvram_push_config = NULL; - /* Free the object list */ vm_object_free_list(vm); @@ -421,6 +402,10 @@ } } + /* Remove the IRQ routing vectors */ + vm->set_irq = NULL; + vm->clear_irq = NULL; + /* Delete the VTTY for Console and AUX ports */ vm_log(vm,"VM","deleting VTTY.\n"); vm_delete_vtty(vm); @@ -448,7 +433,11 @@ /* Remove the lock file */ vm_release_lock(vm,TRUE); + /* Free all chunks */ + vm_chunk_free_all(vm); + /* Free various elements */ + free(vm->rommon_vars.filename); free(vm->ghost_ram_filename); free(vm->sym_filename); free(vm->ios_image); @@ -478,12 +467,14 @@ len = vm->ram_size * 1048576; - if (vm->ghost_status == VM_GHOST_RAM_USE) - return(dev_ram_ghost_init(vm,"ram",vm->ghost_ram_filename,paddr,len)); + if (vm->ghost_status == VM_GHOST_RAM_USE) { + return(dev_ram_ghost_init(vm,"ram",vm->sparse_mem,vm->ghost_ram_filename, + paddr,len)); + } return(dev_ram_init(vm,"ram",vm->ram_mmap, (vm->ghost_status != VM_GHOST_RAM_GENERATE), - vm->ghost_ram_filename,paddr,len)); + vm->ghost_ram_filename,vm->sparse_mem,paddr,len)); } /* Initialize VTTY */ @@ -518,11 +509,11 @@ * Add this device to the device array. The index in the device array * is used by the MTS subsystem. */ - for(i=0;idev_array[i]) break; - if (i == MIPS64_DEVICE_MAX) { + if (i == VM_DEVICE_MAX) { fprintf(stderr,"VM%u: vm_bind_device: device table full.\n", vm->instance_id); return(-1); @@ -550,7 +541,7 @@ { u_int i; - if (!dev->pprev) + if (!dev || !dev->pprev) return(-1); /* Remove the device from the linked list */ @@ -560,7 +551,7 @@ *(dev->pprev) = dev->next; /* Remove the device from the device array */ - for(i=0;idev_array[i] == dev) { vm->dev_array[i] = NULL; break; @@ -600,34 +591,12 @@ return(0); } -/* Set an IRQ for a VM */ -void vm_set_irq(vm_instance_t *vm,u_int irq) -{ - if (vm->boot_cpu->irq_disable) { - vm->boot_cpu->irq_pending = 0; - return; - } - - /* TODO: IRQ routing */ - mips64_set_irq(vm->boot_cpu,irq); - - if (vm->boot_cpu->irq_idle_preempt[irq]) - mips64_idle_break_wait(vm->boot_cpu); -} - -/* Clear an IRQ for a VM */ -void vm_clear_irq(vm_instance_t *vm,u_int irq) -{ - /* TODO: IRQ routing */ - mips64_clear_irq(vm->boot_cpu,irq); -} - /* Suspend a VM instance */ int vm_suspend(vm_instance_t *vm) { if (vm->status == VM_STATUS_RUNNING) { cpu_group_save_state(vm->cpu_group); - cpu_group_set_state(vm->cpu_group,MIPS_CPU_SUSPENDED); + cpu_group_set_state(vm->cpu_group,CPU_STATE_SUSPENDED); vm->status = VM_STATUS_SUSPENDED; } return(0); @@ -658,6 +627,188 @@ usleep(200000); } +/* Create a new chunk */ +static vm_chunk_t *vm_chunk_create(vm_instance_t *vm) +{ + vm_chunk_t *chunk; + size_t area_len; + + if (!(chunk = malloc(sizeof(*chunk)))) + return NULL; + + area_len = VM_CHUNK_AREA_SIZE * VM_PAGE_SIZE; + + if (!(chunk->area = m_memalign(VM_PAGE_SIZE,area_len))) { + free(chunk); + return NULL; + } + + chunk->page_alloc = 0; + chunk->page_total = VM_CHUNK_AREA_SIZE; + + chunk->next = vm->chunks; + vm->chunks = chunk; + return chunk; +} + +/* Free a chunk */ +static void vm_chunk_free(vm_chunk_t *chunk) +{ + free(chunk->area); + free(chunk); +} + +/* Free all chunks used by a VM */ +static void vm_chunk_free_all(vm_instance_t *vm) +{ + vm_chunk_t *chunk,*next; + + for(chunk=vm->chunks;chunk;chunk=next) { + next = chunk->next; + vm_chunk_free(chunk); + } + + vm->chunks = NULL; +} + +/* Allocate an host page */ +void *vm_alloc_host_page(vm_instance_t *vm) +{ + vm_chunk_t *chunk = vm->chunks; + void *ptr; + + if (!chunk || (chunk->page_alloc == chunk->page_total)) { + chunk = vm_chunk_create(vm); + if (!chunk) return NULL; + } + + ptr = chunk->area + (chunk->page_alloc * VM_PAGE_SIZE); + chunk->page_alloc++; + return(ptr); +} + +/* Free resources used by a ghost image */ +static void vm_ghost_image_free(vm_ghost_image_t *img) +{ + if (img) { + if (img->fd != -1) { + close(img->fd); + + if (img->area_ptr != NULL) + munmap(img->area_ptr,img->file_size); + } + + free(img->filename); + free(img); + } +} + +/* Find a specified ghost image in the pool */ +static vm_ghost_image_t *vm_ghost_image_find(char *filename) +{ + vm_ghost_image_t *img; + + for(img=vm_ghost_pool;img;img=img->next) + if (!strcmp(img->filename,filename)) + return img; + + return NULL; +} + +/* Load a new ghost image */ +static vm_ghost_image_t *vm_ghost_image_load(char *filename) +{ + vm_ghost_image_t *img; + + if (!(img = calloc(1,sizeof(*img)))) + return NULL; + + img->fd = -1; + + if (!(img->filename = strdup(filename))) { + vm_ghost_image_free(img); + return NULL; + } + + img->fd = memzone_open_file(img->filename,&img->area_ptr,&img->file_size); + + if (img->fd == -1) { + vm_ghost_image_free(img); + return NULL; + } + + m_log("GHOST","loaded ghost image %s (fd=%d) at addr=%p (size=0x%llx)\n", + img->filename,img->fd,img->area_ptr,(long long)img->file_size); + + return img; +} + +/* Get a ghost image */ +int vm_ghost_image_get(char *filename,u_char **ptr,int *fd) +{ + vm_ghost_image_t *img; + + VM_GLOCK(); + + /* Do we already have this image in the pool ? */ + if ((img = vm_ghost_image_find(filename)) != NULL) { + img->ref_count++; + *ptr = img->area_ptr; + *fd = img->fd; + VM_GUNLOCK(); + return(0); + } + + /* Load the ghost file and add it into the pool */ + if (!(img = vm_ghost_image_load(filename))) { + VM_GUNLOCK(); + fprintf(stderr,"Unable to load ghost image %s\n",filename); + return(-1); + } + + img->ref_count = 1; + *ptr = img->area_ptr; + *fd = img->fd; + + img->next = vm_ghost_pool; + vm_ghost_pool = img; + VM_GUNLOCK(); + return(0); +} + +/* Release a ghost image */ +int vm_ghost_image_release(int fd) +{ + vm_ghost_image_t **img,*next; + + VM_GLOCK(); + + for(img=&vm_ghost_pool;*img;img=&(*img)->next) { + if ((*img)->fd == fd) { + assert((*img)->ref_count > 0); + + (*img)->ref_count--; + + if ((*img)->ref_count == 0) { + m_log("GHOST","unloaded ghost image %s (fd=%d) at " + "addr=%p (size=0x%llx)\n", + (*img)->filename,(*img)->fd,(*img)->area_ptr, + (long long)(*img)->file_size); + + next = (*img)->next; + vm_ghost_image_free(*img); + *img = next; + } + + VM_GUNLOCK(); + return(0); + } + } + + VM_GUNLOCK(); + return(-1); +} + /* Open a VM file and map it in memory */ int vm_mmap_open_file(vm_instance_t *vm,char *name, u_char **ptr,off_t *fsize) @@ -767,15 +918,15 @@ /* Extract IOS configuration from NVRAM and write it to a file */ int vm_nvram_extract_config(vm_instance_t *vm,char *filename) { - char *cfg_buffer; + u_char *cfg_buffer; ssize_t cfg_len; FILE *fd; - if (!vm->nvram_extract_config) + if (!vm->platform->nvram_extract_config) return(-1); /* Extract the IOS configuration */ - if (((cfg_len = vm->nvram_extract_config(vm,&cfg_buffer)) < 0) || + if (((cfg_len = vm->platform->nvram_extract_config(vm,&cfg_buffer)) < 0) || (cfg_buffer == NULL)) return(-1); @@ -796,11 +947,11 @@ /* Read an IOS configuraton from a file and push it to NVRAM */ int vm_nvram_push_config(vm_instance_t *vm,char *filename) { - char *cfg_buffer; + u_char *cfg_buffer; ssize_t len; int res; - if (!vm->nvram_push_config) + if (!vm->platform->nvram_push_config) return(-1); /* Read configuration */ @@ -808,7 +959,7 @@ return(-1); /* Push it! */ - res = vm->nvram_push_config(vm,cfg_buffer,len); + res = vm->platform->nvram_push_config(vm,cfg_buffer,len); free(cfg_buffer); return(res); } @@ -816,6 +967,9 @@ /* Save general VM configuration into the specified file */ void vm_save_config(vm_instance_t *vm,FILE *fd) { + fprintf(fd,"vm create %s %u %s\n", + vm->name,vm->instance_id,vm->platform->name); + if (vm->ios_image) fprintf(fd,"vm set_ios %s %s\n",vm->name,vm->ios_image); @@ -830,4 +984,132 @@ if (vm->vtty_aux_type == VTTY_TYPE_TCP) fprintf(fd,"vm set_aux_tcp_port %s %d\n",vm->name,vm->vtty_aux_tcp_port); + + /* Save slot config */ + vm_slot_save_all_config(vm,fd); +} + +/* Find a platform */ +vm_platform_t *vm_platform_find(char *name) +{ + struct vm_platform_list *p; + + for(p=vm_platforms;p;p=p->next) + if (!strcmp(p->platform->name,name)) + return(p->platform); + + return NULL; +} + +/* Find a platform given its CLI name */ +vm_platform_t *vm_platform_find_cli_name(char *name) +{ + struct vm_platform_list *p; + + for(p=vm_platforms;p;p=p->next) + if (!strcmp(p->platform->cli_name,name)) + return(p->platform); + + return NULL; +} + +/* Register a platform */ +int vm_platform_register(vm_platform_t *platform) +{ + struct vm_platform_list *p; + + if (vm_platform_find(platform->name) != NULL) { + fprintf(stderr,"vm_platform_register: platform '%s' already exists.\n", + platform->name); + return(-1); + } + + if (!(p = malloc(sizeof(*p)))) { + fprintf(stderr,"vm_platform_register: unable to record platform.\n"); + return(-1); + } + + p->platform = platform; + p->next = vm_platforms; + vm_platforms = p; + return(0); +} + +/* Create an instance of the specified type */ +vm_instance_t *vm_create_instance(char *name,int instance_id,char *type) +{ + vm_platform_t *platform; + vm_instance_t *vm = NULL; + + if (!(platform = vm_platform_find(type))) { + fprintf(stderr,"VM %s: unknown platform '%s'\n",name,type); + goto error; + } + + /* Create a generic VM instance */ + if (!(vm = vm_create(name,instance_id,platform))) + goto error; + + /* Initialize specific parts */ + if (vm->platform->create_instance(vm) == -1) + goto error; + + return vm; + + error: + fprintf(stderr,"VM %s: unable to create instance!\n",name); + vm_free(vm); + return NULL; +} + +/* Free resources used by a VM instance */ +static int vm_reg_delete_instance(void *data,void *arg) +{ + vm_instance_t *vm = data; + return(vm->platform->delete_instance(vm)); +} + +/* Delete a VM instance */ +int vm_delete_instance(char *name) +{ + return(registry_delete_if_unused(name,OBJ_TYPE_VM, + vm_reg_delete_instance,NULL)); +} + +/* Initialize a VM instance */ +int vm_init_instance(vm_instance_t *vm) +{ + return(vm->platform->init_instance(vm)); +} + +/* Stop a VM instance */ +int vm_stop_instance(vm_instance_t *vm) +{ + return(vm->platform->stop_instance(vm)); +} + +/* Delete all VM instances */ +int vm_delete_all_instances(void) +{ + return(registry_delete_type(OBJ_TYPE_VM,vm_reg_delete_instance,NULL)); +} + +/* Save configurations of all VM instances */ +static void vm_reg_save_config(registry_entry_t *entry,void *opt,int *err) +{ + vm_instance_t *vm = entry->data; + FILE *fd = opt; + + vm_save_config(vm,fd); + + /* Save specific platform options */ + if (vm->platform->save_config != NULL) + vm->platform->save_config(vm,fd); +} + +/* Save all VM configs */ +int vm_save_config_all(FILE *fd) +{ + registry_foreach_type(OBJ_TYPE_VM,vm_reg_save_config,fd,NULL); + return(0); }