/*
    Copyright 2001 (c) by Claudio Jeker,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/VMemory/Attic/vm_domain.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/
#include "Support.h"
#include "Assert.h"
#include "Error.h"
#include "TMProtectionDomain.h"
#include "MemoryLayout.h"

#include "vm.h"
#include "vm_bootup.h"
#include "vm_calls.h"
#include "kalloc_api.h"
#include "Memory.h"
#include "vm_hal.h"

#include "vm_domain.h"


void vm_domain_init(struct protection_domain *pd, address_t start, address_t end)
{
  struct vm_domain *d;

  printf("vm_domain_init\n");
  d = &pd->vm_space;
  zeroOut(d, sizeof(struct vm_domain) );

  vm_map_init(&d->vm_map, start, end);

  if ( vm_pmap_create(&d->vm_pmap, pd) != VM_OK )
  {
    PANIC("vm_pmap_create failed");
  }

  if (mapSpecialRegions(&pd->vm_space.vm_map, &pd->vm_space.vm_pmap, MSR_MAP_ALL) != VM_OK)
    WARNING("mapSpecialRegions failed");
}

void vm_domain_destroy(struct vm_domain *d)
{
  /* here we should free all memory */

  if ( vm_pmap_destroy(&d->vm_pmap) != VM_OK )
  {
    PANIC("vm_pmap_destroy failed");
  }

}

int vm_domain_resize(struct vm_domain *d, address_t start, address_t end)
{
  return VM_NIMP;
}

extern void km_set_heap_region(struct vm_map* vm_map, struct vm_pmap* vm_pmap,
                                 address_t phys, address_t virt, size_t size);

static void set_loan_region(struct vm_map* vm_map, struct vm_pmap* vm_pmap,
                            int protection, int type, char *name)
{
  Region_t reg;
  size_t size;
  address_t hint;
  int err;

  hint = (address_t)0x10000000;
  size = 0x100000;

  /* XXX perhaps a name would be cool */
  reg.obj_arg.argsize = 0;
  reg.obj_arg.arg[0] = 0;

  reg.start = NULL;
  reg.end = (address_t)size - 1;
  /* VM uses the last valid address and not the first invalid */
  reg.cur = (unsigned char)protection;
  reg.max = (unsigned char)protection;
  reg.type = (unsigned short)type;

  err = vm_region_add_near(vm_map, vm_pmap, &reg, hint, 1);
  switch ( err )
  {
  case VM_OK:
    break;
  default:
    printf("\tmapping FAILED error %i\n", err);
    PANIC("vm_domain_create_kernel: set_loan_region -> vm_region_add failed");
  }

  printf("%s\t-> address %p, size %#lx, type %04hx\n",
         name?name:"anonymous", reg.start, size, type);

  err = vm_region_set_loan(vm_map, vm_pmap, &reg, NULL);
  switch ( err )
  {
  case VM_OK:
    break;
  default:
    printf("\tsetting loan-out object FAILED error %i\n", err);
    PANIC("vm_domain_create_kernel: set_loan_region -> vm_region_set_loan failed");
  }
}

static void set_region(struct vm_map* vm_map, struct vm_pmap* vm_pmap,
                       address_t start, size_t size, offset_t offset, size_t filesize,
                       int protection, int type, char *name)
{
  RegionPtr_t	r;
  int r_len;
  int len;
  int namelen;
  int err;
  unsigned char *arg;

  r = 0;
  r_len = sizeof(Region_t);

  ENSURE( PAGEREMAINDER(offset) == 0 );
  ENSURE( PAGEREMAINDER(start) == 0 );
  size = PAGEROUNDUP(size);

  switch ( type )
  {
  case VM_MANAGER_ANON:
  case VM_MANAGER_STEAL:
    r = km_alloc(r_len, KM_MEMORY);
    if ( r == NULL )
    {
      PANIC("UPS no more memory left");
    }
    /* XXX perhaps a name would be cool */
    r->obj_arg.argsize = 0;
    r->obj_arg.arg[0] = 0;
    break;
  case VM_MANAGER_FILE:
    namelen = str_len(name) + 1;
    if ( namelen > 255 )
    {
      PANIC("UPS filename to long");
    }
    len = namelen + 2;
    len += sizeof(offset_t) + 2;
    len += sizeof(address_t) + 2;
    len += sizeof(size_t) + 2;
    len += 2;
    r = km_alloc(r_len + len, KM_MEMORY);
    if ( r == NULL )
    {
      PANIC("UPS no more memory left");
    }
    arg = r->obj_arg.arg;
    *arg++ = namelen;
    *arg++ = VM_FILE_NAME;
    str_cpy(arg, name);
    arg += namelen;
    *arg++ = sizeof(offset_t);
    *arg++ = VM_FILE_OFFSET;
    byteCopy(arg, &offset, sizeof(offset_t));
    arg += sizeof(offset_t);
    /* sepcial for direct mapped files */
    *arg++ = 0;
    *arg++ = VM_FILE_DIRECT_MAPPED;
    *arg++ = sizeof(address_t);
    *arg++ = VM_FILE_MAP_START;
    byteCopy(arg, &start, sizeof(address_t));
    arg += sizeof(address_t);
    *arg++ = sizeof(size_t);
    *arg++ = VM_FILE_MAP_SIZE;
    byteCopy(arg, &filesize, sizeof(size_t));
    arg += sizeof(size_t);
    r->obj_arg.argsize = len;
    ASSERT( len == (int)((address_t)arg - (address_t)r->obj_arg.arg) );
    break;
  default:
    PANIC("unknown object type");
  }
  ASSERT(r != NULL);

  r->start = (address_t)PAGEROUNDDOWN(start);
  r->end = start + size - 1; /* VM uses the last valid address and not the first invalid */
  r->cur = (unsigned char)protection;
  r->max = (unsigned char)protection;
  r->type = (unsigned short)type;

  printf("%s\t-> address %p, size %#lx offset %#lx\n",
         name?name:"anonymous", r->start, size, offset);

  err = vm_region_add(vm_map, vm_pmap, r);
  switch ( err )
  {
  case VM_OK:
    break;
  default:
    printf("\tmapping FAILED error %i\n", err);
    PANIC("vm_domain_create_kernel: set_region -> vm_region_add failed");
  }

  if ( type == VM_MANAGER_STEAL )
  {
    /* the steal region does not need to be mapped */
    km_free_type(r, KM_MEMORY);
    return;
  }

  err = vm_region_map(vm_map, vm_pmap, r, r->start, r->end, (address_t)-1);
  switch ( err )
  {
  case VM_OK:
    break;
  default:
    printf("\tmapping FAILED error %i\n", err);
    PANIC("vm_domain_create_kernel: set_region -> vm_region_map failed");
  }
  km_free_type(r, KM_MEMORY);
}

pdom_t vm_domain_init_kernel(pdom_t kernel)
{
  /* allocate protection domain and set the ID to zero */
  ASSERT(kernel != NULL);
  kernel->pdId = 0;
  TAILQ_INIT(&kernel->container);

  /* set some useful defaults */
  str_ncpy(kernel->name, "kernel", MAXNAMESIZE - 1);
  kernel->permission = KERNEL_PERM_MSK;
  kernel->parent_pdId = 0;
  kernel->priority = KERNEL_PRIORITY_HIGH;

  vm->kernel = &kernel->vm_space;

  zeroOut(&kernel->vm_space, sizeof(struct vm_domain) );

  vm_map_init(&kernel->vm_space.vm_map, getSegStart(KERNEL), getSegEnd(KERNEL) );

  if ( vm_pmap_create(&kernel->vm_space.vm_pmap, kernel) != VM_OK )
  {
    PANIC("vm_pmap_create failed");
  }

  return kernel;
}

pdom_t vm_domain_create_kernel(struct vm_segmap *segmap, pdom_t k)
{
  struct vm_map* vm_map;
  struct vm_pmap* vm_pmap;
  pdom_t kernel;
  size_t kernelsize;
  address_t addr;
  int i, end;

  /* allocate protection domain and set the ID to zero */
  //	kernel = km_alloc(sizeof(*k), KM_MEMORY);
  //	ASSERT(kernel != NULL);

  //	byteCopy(kernel, k, sizeof(*k)); /* copy the kernel PD form the bootstack
  //																			to the heap */
  kernel = k;
  vm_map = &kernel->vm_space.vm_map;
  vm_pmap = &kernel->vm_space.vm_pmap;

  kernelsize = (size_t)(segmap->bss - segmap->text) + segmap->bsssize; /* XXX XXX XXX this can break if link.scr changes */

  printf("Kernel Protection Domain: mapping regions ... \n");

  printf("      kernel text : ");
  set_region(vm_map, vm_pmap,
             segmap->text, segmap->textsize,
             0, kernelsize,
             VM_ACCESS_READ|VM_ACCESS_EXEC, VM_MANAGER_FILE, "kernel.bin");

  if ( segmap->data + segmap->datasize == segmap->bss ||
       segmap->bss + segmap->bsssize == segmap->data )
  {
    printf("kernel data & bss : ");
    addr = segmap->data < segmap->bss?segmap->data:segmap->bss;
    set_region(vm_map, vm_pmap,
               addr, segmap->datasize + segmap->bsssize,
               addr - segmap->text, kernelsize,
               VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_FILE, "kernel.bin");
  }
  else
  {
    printf("      kernel data : ");
    set_region(vm_map, vm_pmap,
               segmap->data, segmap->datasize,
               segmap->data - segmap->text, kernelsize,
               VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_FILE, "kernel.bin");
    printf("       kernel bss : ");
    set_region(vm_map, vm_pmap,
               segmap->bss, segmap->bsssize,
               segmap->bss - segmap->text, kernelsize,
               VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_FILE, "kernel.bin");
  }

  printf(" boot & INT stack : ");
  set_region(vm_map, vm_pmap,
             segmap->bootstackbottom, segmap->bootstacksize,
             0, 0,
             VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_ANON, "bootstack");

  if ( segmap->optsegment != NULL && segmap->optsegmentsize != 0 )
  {
    printf(" optional segment : "); /* XXX XXX NAME OF THE SEGMENT NEEDED XXX XXX */
    set_region(vm_map, vm_pmap,
               segmap->optsegment, segmap->optsegmentsize,
               0, segmap->optsegmentsize,
               VM_ACCESS_READ, VM_MANAGER_FILE, "user.bin");
  }

  /* reserve the steal region */
  printf(" internal VM heap : ");
  set_region(vm_map, vm_pmap,
             (address_t)STEAL_START, STEAL_SIZE,
             0, 0,
             VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_STEAL, "stealer");


  end = segmap->bootalloccnt - 1; /* last valid entry is the hint */
  for ( i = 0; i < end; i++ )
  {
    switch ( segmap->bootalloc[i].type )
    {
    case VM_TYPE_VM_STATIC:
      printf("  VM static pages : ");
      set_region(vm_map, vm_pmap,
                 segmap->bootalloc[i].virt, segmap->bootalloc[i].size,
                 0, 0,
                 VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_ANON, NULL);
      break;
    case VM_TYPE_HEAP:
      printf("      kernel heap : ");
      km_set_heap_region(vm_map, vm_pmap,
                         segmap->bootalloc[i].phys,
                         segmap->bootalloc[i].virt,
                         segmap->bootalloc[i].size);
      break;
    }
  }

  /* reserve the steal region */
  //-CJ	printf("\n\n\n\n\n\n********************************************\n");
  printf("    loan out area : ");
  set_loan_region(vm_map, vm_pmap,
                  VM_ACCESS_READ|VM_ACCESS_WRITE, VM_MANAGER_LOAN, "loan-out");
  //-CJ	printf("\n\n\n\n\n\n********************************************\n");

  printf("mapping SlowPort, RegSet... \n");
  /* high level mapping of io regions etc (low level is done in vm_bootup) */
  mapSpecialRegions(vm_map, vm_pmap, MSR_IN_KERNEL | MSR_MAP_HLV);


  //-CJ	vm_page_print_all(VM_SHOW_PAGES);
  //-CJ	vm_pmap_print_all(/*vm_pmap*/);
  return kernel;
}
