--- upstream/dynamips-0.2.6-RC1/vm.c 2007/10/06 16:03:58 2 +++ upstream/dynamips-0.2.7-RC3/vm.c 2007/10/06 16:26:06 9 @@ -1,16 +1,15 @@ /* - * 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 #include #include #include +#include #include #include #include @@ -19,16 +18,30 @@ #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; +/* 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) { @@ -53,6 +66,8 @@ if (obj->next) obj->next->pprev = obj->pprev; *(obj->pprev) = obj->next; + + obj->shutdown(vm,obj->data); } /* Find an object given its name */ @@ -92,6 +107,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) { @@ -118,6 +142,24 @@ 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; + case VM_TYPE_C2600: + machine = "c2600"; + break; + case VM_TYPE_MSFC1: + machine = "msfc1"; + break; + case VM_TYPE_PPC32_TEST: + machine = "ppc32_test"; + break; default: machine = "unknown"; break; @@ -137,6 +179,24 @@ 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; + case VM_TYPE_C2600: + machine = "C2600"; + break; + case VM_TYPE_MSFC1: + machine = "MSFC1"; + break; + case VM_TYPE_PPC32_TEST: + machine = "PPC32_TEST"; break; default: machine = "VM"; @@ -146,6 +206,33 @@ return machine; } +/* 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); + + case VM_TYPE_C2600: + return(0xC8); + + default: + return(0xC6); + } +} + /* Generate a filename for use by the instance */ char *vm_build_filename(vm_instance_t *vm,char *name) { @@ -235,9 +322,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 */ @@ -256,17 +345,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); @@ -296,13 +387,15 @@ } 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; + vm->instance_id = instance_id; + vm->type = machine_type; + 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; if (!(vm->name = strdup(name))) { fprintf(stderr,"VM %s: unable to store instance name!\n",name); @@ -376,6 +469,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); @@ -403,7 +500,11 @@ /* Remove the lock file */ vm_release_lock(vm,TRUE); + /* Free all chunks */ + vm_chunk_free_all(vm); + /* Free various elements */ + free(vm->ghost_ram_filename); free(vm->sym_filename); free(vm->ios_image); free(vm->ios_config); @@ -425,6 +526,23 @@ return(registry_unref(vm->name,OBJ_TYPE_VM)); } +/* Initialize RAM */ +int vm_ram_init(vm_instance_t *vm,m_uint64_t paddr) +{ + m_uint32_t len; + + len = vm->ram_size * 1048576; + + 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,vm->sparse_mem,paddr,len)); +} + /* Initialize VTTY */ int vm_init_vtty(vm_instance_t *vm) { @@ -457,11 +575,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); @@ -489,7 +607,7 @@ { u_int i; - if (!dev->pprev) + if (!dev || !dev->pprev) return(-1); /* Remove the device from the linked list */ @@ -499,7 +617,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; @@ -539,34 +657,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); @@ -597,6 +693,241 @@ 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) +{ + char *filename; + int fd; + + if (!(filename = vm_build_filename(vm,name))) { + fprintf(stderr,"vm_mmap_open_file: unable to create filename (%s)\n", + name); + return(-1); + } + + if ((fd = memzone_open_file(filename,ptr,fsize)) == -1) + fprintf(stderr,"vm_mmap_open_file: unable to open file '%s' (%s)\n", + filename,strerror(errno)); + + free(filename); + return(fd); +} + +/* Open/Create a VM file and map it in memory */ +int vm_mmap_create_file(vm_instance_t *vm,char *name,size_t len,u_char **ptr) +{ + char *filename; + int fd; + + if (!(filename = vm_build_filename(vm,name))) { + fprintf(stderr,"vm_mmap_create_file: unable to create filename (%s)\n", + name); + return(-1); + } + + if ((fd = memzone_create_file(filename,len,ptr)) == -1) + fprintf(stderr,"vm_mmap_create_file: unable to open file '%s' (%s)\n", + filename,strerror(errno)); + + free(filename); + return(fd); +} + +/* Close a memory mapped file */ +int vm_mmap_close_file(int fd,u_char *ptr,size_t len) +{ + if (ptr != NULL) + munmap(ptr,len); + + if (fd != -1) + close(fd); + + return(0); +} + /* Save the Cisco IOS configuration from NVRAM */ int vm_ios_save_config(vm_instance_t *vm) {