--- upstream/dynamips-0.2.6-RC5/device.c 2007/10/06 16:09:07 6 +++ upstream/dynamips-0.2.7-RC1/device.c 2007/10/06 16:23:47 7 @@ -1,5 +1,5 @@ /* - * Cisco 7200 (Predator) simulation platform. + * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) */ @@ -12,20 +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 /* 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]); @@ -113,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; } + } - /* reinitialize the device to a clean state */ - dev_init(dev); + 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); + + 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 */ @@ -173,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]; @@ -204,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; @@ -227,23 +245,27 @@ dev->phys_len = len; dev->flags = VDEVICE_FLAG_CACHING; - if (filename) { - dev->fd = memzone_create_file(filename,dev->phys_len,&ram_ptr); - - if (dev->fd == -1) { - perror("dev_create_ram: mmap"); + 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; } - - 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; + dev_sparse_init(dev); } vm_bind_device(vm,dev); @@ -252,7 +274,7 @@ /* Create a ghosted RAM device */ struct vdevice * -dev_create_ghost_ram(vm_instance_t *vm,char *name,char *filename, +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; @@ -263,18 +285,28 @@ dev->phys_addr = paddr; dev->phys_len = len; - dev->flags = VDEVICE_FLAG_CACHING; + dev->flags = VDEVICE_FLAG_CACHING|VDEVICE_FLAG_GHOST; - 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 (!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; + } - if (!(dev->host_addr = (m_iptr_t)ram_ptr)) { - free(dev); - return NULL; + dev->host_addr = (m_iptr_t)ram_ptr; + dev_sparse_init(dev); } vm_bind_device(vm,dev); @@ -294,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); @@ -310,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) {