--- upstream/dynamips-0.2.6-RC2/device.c 2007/10/06 16:05:34 3 +++ upstream/dynamips-0.2.7/device.c 2007/10/06 16:29:14 10 @@ -1,5 +1,5 @@ /* - * Cisco 7200 (Predator) simulation platform. + * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ @@ -12,51 +12,20 @@ #include #include #include +#include -#include "mips64.h" #include "cpu.h" +#include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" -#include "cp0.h" #define DEBUG_DEV_ACCESS 0 -/* Map a memory zone from a file */ -u_char *memzone_map_file(int fd,size_t len) -{ - return(mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(off_t)0)); -} - -/* Create a file to serve as a memory zone */ -int memzone_create_file(char *filename,size_t len,u_char **ptr) -{ - int fd; - - if ((fd = open(filename,O_CREAT|O_RDWR,S_IRWXU)) == -1) { - perror("memzone_create_file: open"); - return(-1); - } - - if (ftruncate(fd,len) == -1) { - perror("memzone_create_file: ftruncate"); - return(-1); - } - - *ptr = memzone_map_file(fd,len); - - if (!*ptr) { - close(fd); - fd = -1; - } - - return(fd); -} - /* Get device by ID */ struct vdevice *dev_get_by_id(vm_instance_t *vm,u_int dev_id) { - if (!vm || (dev_id >= MIPS64_DEVICE_MAX)) + if (!vm || (dev_id >= VM_DEVICE_MAX)) return NULL; return(vm->dev_array[dev_id]); @@ -144,40 +113,56 @@ /* Remove a device */ void dev_remove(vm_instance_t *vm,struct vdevice *dev) { - if (dev != NULL) { - vm_unbind_device(vm,dev); + if (dev == NULL) + return; + + vm_unbind_device(vm,dev); - vm_log(vm,"DEVICE", - "Removal of device %s, fd=%d, host_addr=0x%llx, flags=%d\n", - dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->flags); - - if (dev->fd != -1) { - /* Unmap memory mapped file */ - if (dev->host_addr && !(dev->flags & VDEVICE_FLAG_REMAP)) { - if (dev->flags & VDEVICE_FLAG_SYNC) { - msync((void *)dev->host_addr,dev->phys_len, - MS_SYNC|MS_INVALIDATE); - } - - vm_log(vm,"MMAP","unmapping of device '%s', " - "fd=%d, host_addr=0x%llx, len=0x%x\n", - dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->phys_len); - munmap((void *)dev->host_addr,dev->phys_len); - } + vm_log(vm,"DEVICE", + "Removal of device %s, fd=%d, host_addr=0x%llx, flags=%d\n", + dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->flags); - if (dev->flags & VDEVICE_FLAG_SYNC) - fsync(dev->fd); + if (dev->flags & VDEVICE_FLAG_REMAP) { + dev_init(dev); + return; + } - close(dev->fd); - } else { - /* Use of malloc'ed host memory: free it */ - if (dev->host_addr && !(dev->flags & VDEVICE_FLAG_REMAP)) - free((void *)dev->host_addr); + if (dev->flags & VDEVICE_FLAG_SPARSE) { + dev_sparse_shutdown(dev); + + if (dev->flags & VDEVICE_FLAG_GHOST) { + vm_ghost_image_release(dev->fd); + dev_init(dev); + return; + } + } + + if (dev->fd != -1) { + /* Unmap memory mapped file */ + if (dev->host_addr) { + if (dev->flags & VDEVICE_FLAG_SYNC) { + msync((void *)dev->host_addr,dev->phys_len, + MS_SYNC|MS_INVALIDATE); + } + + vm_log(vm,"MMAP","unmapping of device '%s', " + "fd=%d, host_addr=0x%llx, len=0x%x\n", + dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->phys_len); + munmap((void *)dev->host_addr,dev->phys_len); } + + if (dev->flags & VDEVICE_FLAG_SYNC) + fsync(dev->fd); - /* reinitialize the device to a clean state */ - dev_init(dev); + close(dev->fd); + } else { + /* Use of malloc'ed host memory: free it */ + if (dev->host_addr) + free((void *)dev->host_addr); } + + /* reinitialize the device to a clean state */ + dev_init(dev); } /* Show properties of a device */ @@ -204,7 +189,7 @@ } /* device access function */ -void *dev_access(cpu_mips_t *cpu,u_int dev_id,m_uint32_t offset, +void *dev_access(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data) { struct vdevice *dev = cpu->vm->dev_array[dev_id]; @@ -217,6 +202,15 @@ return(dev->handler(cpu,dev,offset,op_size,op_type,data)); } +/* Synchronize memory for a memory-mapped (mmap) device */ +int dev_sync(struct vdevice *dev) +{ + if (!dev || !dev->host_addr) + return(-1); + + return(msync((void *)dev->host_addr,dev->phys_len,MS_SYNC)); +} + /* Remap a device at specified physical address */ struct vdevice *dev_remap(char *name,struct vdevice *orig, m_uint64_t paddr,m_uint32_t len) @@ -226,17 +220,19 @@ if (!(dev = dev_create(name))) return NULL; - dev->phys_addr = paddr; - dev->phys_len = len; - dev->flags = orig->flags | VDEVICE_FLAG_REMAP; - dev->fd = orig->fd; - dev->host_addr = orig->host_addr; - dev->handler = orig->handler; + dev->phys_addr = paddr; + dev->phys_len = len; + dev->flags = orig->flags | VDEVICE_FLAG_REMAP; + dev->fd = orig->fd; + dev->host_addr = orig->host_addr; + dev->handler = orig->handler; + dev->sparse_map = orig->sparse_map; return dev; } /* Create a RAM device */ -struct vdevice *dev_create_ram(vm_instance_t *vm,char *name,char *filename, +struct vdevice *dev_create_ram(vm_instance_t *vm,char *name, + int sparse,char *filename, m_uint64_t paddr,m_uint32_t len) { struct vdevice *dev; @@ -249,13 +245,70 @@ dev->phys_len = len; dev->flags = VDEVICE_FLAG_CACHING; - if (filename) { - dev->fd = memzone_create_file(filename,dev->phys_len,&ram_ptr); - dev->host_addr = (m_iptr_t)ram_ptr; + if (!sparse) { + if (filename) { + dev->fd = memzone_create_file(filename,dev->phys_len,&ram_ptr); + + if (dev->fd == -1) { + perror("dev_create_ram: mmap"); + free(dev); + return NULL; + } + + dev->host_addr = (m_iptr_t)ram_ptr; + } else { + dev->host_addr = (m_iptr_t)m_memalign(4096,dev->phys_len); + } + + if (!dev->host_addr) { + free(dev); + return NULL; + } } else { - dev->host_addr = (m_iptr_t)m_memalign(4096,dev->phys_len); + dev_sparse_init(dev); } - + + vm_bind_device(vm,dev); + return dev; +} + +/* Create a ghosted RAM device */ +struct vdevice * +dev_create_ghost_ram(vm_instance_t *vm,char *name,int sparse,char *filename, + m_uint64_t paddr,m_uint32_t len) +{ + struct vdevice *dev; + u_char *ram_ptr; + + if (!(dev = dev_create(name))) + return NULL; + + dev->phys_addr = paddr; + dev->phys_len = len; + dev->flags = VDEVICE_FLAG_CACHING|VDEVICE_FLAG_GHOST; + + if (!sparse) { + dev->fd = memzone_open_cow_file(filename,dev->phys_len,&ram_ptr); + if (dev->fd == -1) { + perror("dev_create_ghost_ram: mmap"); + free(dev); + return NULL; + } + + if (!(dev->host_addr = (m_iptr_t)ram_ptr)) { + free(dev); + return NULL; + } + } else { + if (vm_ghost_image_get(filename,&ram_ptr,&dev->fd) == -1) { + free(dev); + return NULL; + } + + dev->host_addr = (m_iptr_t)ram_ptr; + dev_sparse_init(dev); + } + vm_bind_device(vm,dev); return dev; } @@ -273,12 +326,6 @@ return NULL; } - if (orig_dev->fd == -1) { - fprintf(stderr,"VM%u: dev_create_ram_alias: device %s has no FD.\n", - vm->instance_id,orig_dev->name); - return NULL; - } - if (!(dev = dev_remap(name,orig_dev,paddr,len))) { fprintf(stderr,"VM%u: dev_create_ram_alias: unable to create " "new device %s.\n",vm->instance_id,name); @@ -289,8 +336,119 @@ return dev; } +/* Initialize a sparse device */ +int dev_sparse_init(struct vdevice *dev) +{ + u_int i,nr_pages; + size_t len; + + /* create the sparse mapping */ + nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT); + len = nr_pages * sizeof(m_iptr_t); + + if (!(dev->sparse_map = malloc(len))) + return(-1); + + if (!dev->host_addr) { + memset(dev->sparse_map,0,len); + } else { + for(i=0;isparse_map[i] = dev->host_addr + (i << VM_PAGE_SHIFT); + } + + dev->flags |= VDEVICE_FLAG_SPARSE; + return(0); +} + +/* Shutdown sparse device structures */ +int dev_sparse_shutdown(struct vdevice *dev) +{ + if (!(dev->flags & VDEVICE_FLAG_SPARSE)) + return(-1); + + free(dev->sparse_map); + dev->sparse_map = NULL; + return(0); +} + +/* Show info about a sparse device */ +int dev_sparse_show_info(struct vdevice *dev) +{ + u_int i,nr_pages,dirty_pages; + + printf("Sparse information for device '%s':\n",dev->name); + + if (!(dev->flags & VDEVICE_FLAG_SPARSE)) { + printf("This is not a sparse device.\n"); + return(-1); + } + + if (!dev->sparse_map) { + printf("No sparse map.\n"); + return(-1); + } + + nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT); + dirty_pages = 0; + + for(i=0;isparse_map[i] & VDEVICE_PTE_DIRTY) + dirty_pages++; + + printf("%u dirty pages on a total of %u pages.\n",dirty_pages,nr_pages); + return(0); +} + +/* Get an host address for a sparse device */ +m_iptr_t dev_sparse_get_host_addr(vm_instance_t *vm,struct vdevice *dev, + m_uint64_t paddr,u_int op_type,int *cow) +{ + m_iptr_t ptr,ptr_new; + u_int offset; + + offset = (paddr - dev->phys_addr) >> VM_PAGE_SHIFT; + ptr = dev->sparse_map[offset]; + *cow = 0; + + /* + * If the device is not in COW mode, allocate a host page if the physical + * page is requested for the first time. + */ + if (!dev->host_addr) { + if (!(ptr & VDEVICE_PTE_DIRTY)) { + ptr = (m_iptr_t)vm_alloc_host_page(vm); + assert(ptr); + + dev->sparse_map[offset] = ptr | VDEVICE_PTE_DIRTY; + return(ptr); + } + + return(ptr & VM_PAGE_MASK); + } + + /* + * We have a "ghost" base. We apply the copy-on-write (COW) mechanism + * ourselves. + */ + if (ptr & VDEVICE_PTE_DIRTY) + return(ptr & VM_PAGE_MASK); + + if (op_type == MTS_READ) { + *cow = 1; + return(ptr & VM_PAGE_MASK); + } + + /* Write attempt on a "ghost" page. Duplicate it */ + ptr_new = (m_iptr_t)vm_alloc_host_page(vm); + assert(ptr_new); + + memcpy((void *)ptr_new,(void *)(ptr & VM_PAGE_MASK),VM_PAGE_SIZE); + dev->sparse_map[offset] = ptr_new | VDEVICE_PTE_DIRTY; + return(ptr_new); +} + /* dummy console handler */ -static void *dummy_console_handler(cpu_mips_t *cpu,struct vdevice *dev, +static void *dummy_console_handler(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size, u_int op_type,m_uint64_t *data) {