/*
    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/mips/Attic/vm_pmap.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 "MemoryLayout.h"
#include "TMProtectionDomain.h"

#include "vm.h"
#include "vm_alloc.h"

#include "cpu.h"
#include "tlb.h"

#include "vm_pmap.h"

/* MIPS does not have a predefined structure for the memory mapping.
	 So a reverse table is implemented.
	 The fine thing of a reverse table is that the table is almost 
	 architecture independent.
	 
	 How is it implemented:
	 There are mainly two tables:
	 - the physical table: here the page number and the page flags are stored
	     also a reference counter is needed.
	 - the virtual hashtable: here the virtual page number is stored but also
	     a pointer to the pmap structure or the corresponding protection
			 domain is needed. Also needed are a pointer to the physical table 
			 entry and a pointer to link the hash entries together.
	 
	 The hardest thing is to figure out how big the hash table has to be.
	 If the hash is to small we perform slow lookups if to big we waste memory.
	 At the moment a table once ore twice as big as the physical table seems OK.
	 
	 XXX add stistic functions to control the usage of the hash...
 */

//#define PMAP_DEBUG_ON
#define PMAP_DEBUG_OFF

#ifdef PMAP_DEBUG_ON
#	define PMAP_DEBUG(x)															\
		do {																		\
			printf("PMAP: ");														\
			printf x;																\
			printf("\n");															\
		} while (0)
#else
#	define PMAP_DEBUG(x)															\
		while (0) {																	\
			printf("PMAP: ");														\
			printf x;																\
			printf("\n");															\
		}
#endif



typedef struct pmap_pentry
{
  Register tlblo;
  unsigned long count;
}
pmap_pentry_t;

#define PMAP_PENTRY_GLOBAL		TLB_GLOBAL_R3k
#define PMAP_PENTRY_VALID			TLB_VALID_R3k
#define PMAP_PENTRY_DIRTY			TLB_DIRTY_R3k
#define PMAP_PENTRY_NONCACHE	TLB_NOCACHE_R3k
#define PMAP_PENTRY_MASK			0x0F00	/* XXX */

#define PMAP_PENTRY_INIT(pnum) ((pnum) << 12)

#define PMAP_ENTRY_SET(tlb, flag) ((tlb) |= (flag))
#define PMAP_ENTRY_CLEAR(tlb, flag) ((tlb) &= ~(flag))
#define PMAP_ENTRY_ISSET(tlb, flag) ((tlb) & (flag))

#define PMAP_PENTRY_PHYS(tlblo) ((tlblo) & TLB_LO_PFN_MASK_R3k)

typedef struct pmap_ventry
{
  Register tlbhi;
  struct vm_pmap *vm_pmap;
  struct pmap_pentry *pentry;
  struct pmap_ventry *next;
}
pmap_ventry_t;

#define PMAP_VENTRY_VALID			0x0001
#define PMAP_VENTRY_WRITE			0x0400

#define PMAP_VENTRY_FREE(tlbhi) (((tlbhi) & PMAP_VENTRY_VALID) == 0)
#define PMAP_VENTRY_VIRT(tlbhi) ((address_t)((tlbhi) & TLB_HI_VPN_MASK_R3k))

#define PMAP_VENTRY_TLB(tlbhi, pid) \
	(((tlbhi)&TLB_HI_VPN_MASK_R3k) | \
	 (((pid)<<TLB_HI_PID_SHIFT_R3k)&TLB_HI_PID_MASK_R3k))

#define PMAP_VENTRY_HASH(virt, pmap) \
		((PAGENUMBER(virt)+(unsigned long)pmap) & (vm_pmap_state.vhashmask))

/* a few global addresses and figures need to be stored */
struct vm_pmap_state
{
  pmap_ventry_t* vtable;	/* hash table */
  unsigned long vhashmask;
  unsigned long vhashsize;
  pmap_ventry_t* vhint;		/* hint into the vspace for faster access */
  pmap_pentry_t* ptable;	/* physical table */
  pmap_ventry_t* vspace;	/* additional space for virtual entries */
  pmap_ventry_t* vend;		/* end of space */
  size_t numofpages;
  /* XXX we need a possibility to extend the state */
}
vm_pmap_state;

void vm_pmap_print_entry(pmap_ventry_t* v);
/* internal function */

/* This function allocates the needed Memory for the pmap forward or reverse table.
	 The mappings for the Kernel Protection Domain are added later via normal enter calls.
 */
void vm_pmap_init(struct vm_segmap* segmap)
{
  struct vm_physmap *physmap;
  address_t region;
  size_t freepages;
  size_t hashsize;
  size_t size;
  size_t rounded;
  size_t j;
  int physcnt;
  int i;
  int n;

  vm->pmap = &vm_pmap_state;
  /* STEP 1: estimate the needed space.
  	 We count all pages also the already used ones. So it is possible to 
  	 free them.
   */
  physmap = segmap->physmap;
  physcnt = segmap->physcnt;

  for ( i = 0, freepages = 0; i < physcnt; i++ )
    freepages += physmap[i].end - physmap[i].start;

  /* STEP 2: allocate the needed memory (actually this is more a reserve) */
  size = freepages * (sizeof(pmap_pentry_t) + 2 * sizeof(pmap_ventry_t));
  rounded = vm_allocate_bootup_memory(segmap, &region, size, VM_TYPE_VM_STATIC);
  zeroOut(region, rounded);

  PMAP_DEBUG(("vm_pmap_init: size %lu rounded %lu\n", size, rounded));

  /* STEP 3: divide the ram into the needed areas */
  vm_pmap_state.ptable = (pmap_pentry_t*) region;
  vm_pmap_state.vend = (pmap_ventry_t*)region + size;

  vm_pmap_state.vtable = (pmap_ventry_t*) (region + freepages * sizeof(pmap_pentry_t) );
  size = rounded - freepages * sizeof(pmap_pentry_t);
  for ( hashsize = 1; hashsize <= freepages; hashsize<<=1 )
    ;

  printf("vm_pmap_init: # of pentries %lu, # of ventries %lu, # of vspace entries %lu\n",
         freepages, hashsize, size/sizeof(pmap_ventry_t) - hashsize);
  PMAP_DEBUG(("              size of ptable %lu, vtable %lu, vspace %lu\n",
              sizeof(pmap_pentry_t)*freepages,
              sizeof(pmap_ventry_t)*hashsize,
              size - sizeof(pmap_ventry_t)*hashsize));

  ASSERT( hashsize * sizeof(pmap_ventry_t) < size );

  vm_pmap_state.vhashsize = hashsize;
  vm_pmap_state.vhashmask = hashsize - 1;

  vm_pmap_state.vspace = vm_pmap_state.vtable + hashsize;
  vm_pmap_state.vhint = vm_pmap_state.vspace;
  ASSERT(vm_pmap_state.vspace < vm_pmap_state.vend);

  /* STEP 4: initialize the ptable */
  for ( i = 0, n = 0; i < physcnt; i++ )
    for ( j = physmap[i].start; j < physmap[i].end; j++ )
    {
      vm_pmap_state.ptable[n].tlblo = PMAP_PENTRY_INIT(j);
      vm_pmap_state.ptable[n++].count = 0;
      /* initalize the flags */
    }

  vm->stat.pagesize = PAGESIZE;
  vm->stat.pagemask = PAGEFRAMEMASK;
  vm->stat.pageshift = PAGEBITS;
  vm->stat.faults = 0;
  vm->stat.pmapfaults = 0;
  /* done */
}


/* almost nothing to be done, just initalize the pmap struct. */
int vm_pmap_create(pmap_t pmap, struct protection_domain *d)
{
  /* what infos are needed?
   */
  pmap->pdom = d;
  pmap->pseudopid = d->pdId;
  if ( pmap->pseudopid > TLB_HI_NPID_R3k )
    PANIC("to many Protection Domains for mips");
  return VM_OK;
}

/* XXX do we remove all regions first ??? */
int vm_pmap_destroy(pmap_t pmap)
{
  /* if we need to remove all mappings first the fastest way is
  	 probably to walk through the tree. Definitifly faster is the call
  	 of vm_pmap_region_remove befor calling vm_pmap_destroy.
   */
  /* cleanup, nothing to be done */
  /* XXX remove the pseudopid from a possible cache table */

  return VM_OK;
}

static pmap_ventry_t* get_free_ventry(pmap_ventry_t* start)
{
  pmap_ventry_t* new;
  pmap_ventry_t* hint;

  while ( start->next != NULL )
  {
    start = start->next;
  }

  hint = vm_pmap_state.vhint;
  new = hint;
  while ( !PMAP_VENTRY_FREE(new->tlbhi) )
  {
    if ( new < vm_pmap_state.vend )
    {
      new++;
    }
    else
    {
      new = vm_pmap_state.vspace;
    }
    if ( new == hint )
    {
      /* no free ventry left in the vspace */
      return NULL;
    }
  }

  new->next = NULL;
  start->next = new;

  vm_pmap_state.vhint = new + 1;

  return new;
}

/* enter one virtual to physical mapping
	 Only the reverse table is updated. Acctually a new vtable entry is created
	 and linked to the corresponding ptable entry.
 */
int vm_pmap_enter(pmap_t pmap, address_t virt, address_t phys, int flags)
{
  pmap_pentry_t *pentry;
  pmap_ventry_t *ventry;
  pmap_ventry_t *new;

  /* STEP 1: check ptable */
  /* NOTE: the flags of a page can only be downgraded */
  ASSERT(vm_pmap_state.ptable != NULL);
  ASSERT(vm_pmap_state.vtable != NULL);

  ASSERT( PAGEREMAINDER(virt) == 0 );
  ASSERT( PAGEREMAINDER(phys) == 0 );
  ASSERT( flags & PMAP_READ );

  /* XXX on misp physical addresses are often interpreted as
  	 K0SEG addresses (addresses starting with 0x8xxxxxxx).
  	 Topsy does this heavily and so we have to fix that here
  	 because PAGENUMBER(K0SEG address) returns a different page then
  	 PAGENUMBER(physical address).
  	 Here is the correct place to add this HACK. //-CJ
   */
  if ( ISA_DIRECTMAPPED(phys) )
  {
    WARNING("pmap: physical address was direct mapped");
    PMAP_DEBUG(("\tphys: %p virt %p\n", phys, virt));
    phys = (address_t)K0SEG_TO_PHYS(phys);
  }

  PMAP_DEBUG(("vm_pmap_enter: virt %p, phys %p, pseudopid %i\n",
              virt, phys, pmap->pseudopid));
  //-CJ	printf("vm_pmap_enter: virt %p, phys %p, pseudopid %i\n",
  //-CJ virt, phys, pmap->pseudopid);
  pentry = &vm_pmap_state.ptable[PAGENUMBER(phys)];
  ventry = &vm_pmap_state.vtable[PMAP_VENTRY_HASH(virt, pmap)];

  //-CJ	PMAP_DEBUG(("vm_pmap_enter: pentry @ %p ptable @ %p index %lu\n",
  //-CJ	pentry, vm_pmap_state.ptable, PAGENUMBER(phys)));
  //-CJ	PMAP_DEBUG(("vm_pmap_enter: ventry @ %p vtable @ %p index %lu\n",
  //-CJ	ventry, vm_pmap_state.vtable, PMAP_VENTRY_HASH(virt, pmap)));
  ASSERT( phys == (address_t)PMAP_PENTRY_PHYS(pentry->tlblo) );

  /* XXX lock / interrupt save */

  /* write bit is mostly ignored, the dirty bit is set by the lookup function.
  	 The write bit needs to be stored on the ventry so it is possible to
  	 have read-only sharings of read-write regions. */
  if ( !(flags & PMAP_WRITE) &&
       PMAP_ENTRY_ISSET(pentry->tlblo, PMAP_PENTRY_DIRTY) )
  {
    /* clear dirty bit because this page is no longer globaly read-write */
    PMAP_ENTRY_CLEAR(pentry->tlblo, PMAP_PENTRY_DIRTY);
  }
  else if ( flags & PMAP_WRITE && !PMAP_ENTRY_ISSET(pentry->tlblo, PMAP_PENTRY_VALID))
  {
    /* set dirty bit because this page is globaly read-write */
    PMAP_ENTRY_SET(pentry->tlblo, PMAP_PENTRY_DIRTY);
  }

  /* execute bit is ignored -> not supported on HW. */
  PMAP_ENTRY_SET(pentry->tlblo, PMAP_PENTRY_VALID);
  pentry->count++;

  /* STEP 2: enter vtable */
  if (! PMAP_VENTRY_FREE(ventry->tlbhi) )
  {
    new = get_free_ventry(ventry);
    if ( new == NULL )
    {
      PANIC("VM pmap: out of ventries");
      return VM_NOMEM;
    }
  }
  else
  {
    new = ventry;
  }

  new->tlbhi = (Register)virt;
  PMAP_ENTRY_SET(new->tlbhi, PMAP_VENTRY_VALID);
  if (flags & PMAP_WRITE)
  {
    PMAP_ENTRY_SET(new->tlbhi, PMAP_VENTRY_WRITE);
  }
  new->vm_pmap = pmap;
  new->pentry = pentry;

  /* finally enter the pmap entry into the TLB, we will use it very soon.
  	 NOTE: if the page is not marked dirty even if we have write access.
   */
  //-CJ don't ask me why but I get a illegal access of the tmThread if this is
  //-CJ turned on...
  setR3kTLBRandom(pentry->tlblo,
                  PMAP_VENTRY_TLB(new->tlbhi, pmap->pseudopid));
  //-CJ	vm_pmap_print_entry(new);
  //-CJ	vm_pmap_print(pmap);

  /* XXX unlock / enable interrupts */
  return VM_OK;
}

/* vm_pmap_region_enter: portable version (just calls vm_pmap_enter) */
int vm_pmap_region_enter(struct vm_pmap* vm_pmap, address_t phys, address_t virt, size_t size, int flags)
{
  size_t s;
  int err;

  for ( s = 0; s < size; s += PAGESIZE )
  {
    err = vm_pmap_enter(vm_pmap, virt + s, phys + s, flags);
    switch ( err )
    {
    case VM_OK:
      break;
    default:
      PMAP_DEBUG(("vm_region_map: pmap enter failed %i\n", err));
      return err;
    }
  }
  return VM_OK;
}

/* remove one virtual to physical mapping
	 Make sure that the mapping is no longer in the TLB.
 */
int vm_pmap_page_remove(pmap_t pmap, address_t virt)
{
  pmap_pentry_t *pentry;
  pmap_ventry_t *vprev;
  pmap_ventry_t *ventry;
  address_t vpage;
  Register index;
  Register tlblo;
  Register tlbhi;

  /* STEP 1: find vtable entry */
  vpage = (address_t)PAGEROUNDDOWN(virt);
  ASSERT(vm_pmap_state.vtable != NULL);
  ventry = &vm_pmap_state.vtable[PMAP_VENTRY_HASH(vpage, pmap)];

  vprev = NULL;
  do
  {
    if (PMAP_VENTRY_VIRT(ventry->tlbhi) == vpage && ventry->vm_pmap == pmap)
    {
      pentry = ventry->pentry;
      break;
    }
    if ( ventry->next == NULL )
      goto fail;
    vprev = ventry;
    ventry = ventry->next;
  }
  while (1);

  /* STEP 2: update ptable entry and clear vtable entry */
  if ( --pentry->count <= 0 )
  {
    /* page no longer used, invalidate */
    PMAP_ENTRY_CLEAR(pentry->tlblo, PMAP_PENTRY_MASK);
  }
  tlbhi = ventry->tlbhi;
  ventry->tlbhi = 0;
  ventry->vm_pmap = 0;
  ventry->pentry = 0;
  /* unlink ventry */
  if ( vprev != NULL )
  {
    vprev->next = ventry->next;
    ventry->next = NULL;
  }

  /* STEP 3: check TLB */
  index = probeR3kTLBEntry(PMAP_VENTRY_TLB(tlbhi, pmap->pseudopid));
  if (!(index & TLB_IND_P))
  {
    tlblo = pentry->tlblo;
    //-CJ	printf("vm_pmap_remove_page: Removing TLB[%li](%#lx, %#lx)\n",
    //-CJ index>>TLB_IND_INX_SHIFT_R3k,
    //-CJ tlblo, PMAP_VENTRY_TLB(tlbhi, pmap->pseudopid));
    setR3kTLBEntry(0, 0,
                   index>>TLB_IND_INX_SHIFT_R3k);
  }
  return VM_OK;


fail:
  PMAP_DEBUG(("vm_pmap_page_remove: virt %p not in table\n", virt));
  return VM_FAILED;

}

/* remove all mappings of a region */
int vm_pmap_region_remove(pmap_t pmap, address_t virt_start, address_t virt_end)
{
  address_t i;

  //-CJ	vm_pmap_print(pmap);
  printf("vm_pmap_region_remove: remove mappings from %p - %p\n",
         virt_start, virt_end);
  for ( i = virt_start; i <= virt_end; i += PAGESIZE )
  {
    vm_pmap_page_remove(pmap, i);
  }
  //-CJ	vm_pmap_print(pmap);
  return VM_OK;
}

/* lookup a physical address when the virtual address is known */
int vm_pmap_lookup(pmap_t pmap, address_t virt, int type)
{
  pmap_pentry_t *pentry;
  pmap_ventry_t *ventry;
  address_t vpage;
  Register tlblo;

  /* Nice to know: when setting entries only a downgrade of privileges
  	 is done. So if a page is normaly ro but one process has rw rights
  	 the permission in the pmap ptable is ro. So we will get a protection
  	 fault for writes and then we need to modify the TLB but not the ptable
  	 entry. This sounds very complicated but it isn't. It is also so that in
  	 Topsy multiple sharing of a page with different protection is not the 
  	 common case.
   */

  /* This function is called very often so it needs to be optimal for the
  	 common case. On MIPS this is a successful lookup.
   */

  /* STEP 1: find vtable entry */
  vpage = (address_t)PAGEROUNDDOWN(virt);
  ASSERT(vm_pmap_state.vtable != NULL);
  ventry = &vm_pmap_state.vtable[PMAP_VENTRY_HASH(vpage, pmap)];

  do
  {
    if (PMAP_VENTRY_VIRT(ventry->tlbhi) == vpage && ventry->vm_pmap == pmap)
    {
      pentry = ventry->pentry;
      /* STEP 2: check ptable entry */
      if (! PMAP_ENTRY_ISSET(pentry->tlblo, PMAP_PENTRY_VALID) )
        goto fail;

      /* check protection */
      /* again EXEC ignored, WRITE needs to check the ventry if the page is not dirty */
      if ( type == PMAP_WRITE && !PMAP_ENTRY_ISSET(pentry->tlblo, PMAP_PENTRY_DIRTY) )
      {
        if ( PMAP_ENTRY_ISSET(ventry->tlbhi, PMAP_VENTRY_WRITE ) )
        {
          /* update TLB but not the table because this page is not global read-write */
          PMAP_DEBUG(("vm_pmap_lookup: tlblo: %#lx tlbhi: %#lx TLB dirty\n",
                      pentry->tlblo, PMAP_VENTRY_TLB(ventry->tlbhi, pmap->pseudopid)));
          tlblo = pentry->tlblo;
          PMAP_ENTRY_SET(tlblo, PMAP_PENTRY_DIRTY);
          setR3kTLBRandom(tlblo,
                          PMAP_VENTRY_TLB(ventry->tlbhi, pmap->pseudopid));
          return VM_OK;
        }
        else
        {
          goto fail;
        }
      }
      /* STEP 3: update TLB */
      //-CJ			printf("vm_pmap_lookup: tlblo: %#lx tlbhi: %#lx\n",
      //-CJ					pentry->tlblo, PMAP_VENTRY_TLB(ventry->tlbhi, pmap->pseudopid));
      PMAP_DEBUG(("vm_pmap_lookup: tlblo: %#lx tlbhi: %#lx\n",
                  pentry->tlblo, PMAP_VENTRY_TLB(ventry->tlbhi, pmap->pseudopid)));
      setR3kTLBRandom(pentry->tlblo,
                      PMAP_VENTRY_TLB(ventry->tlbhi, pmap->pseudopid));
      return VM_OK;
    }
    if ( ventry->next == NULL )
      goto fail;
    ventry = ventry->next;
  }
  while (1);

  PANIC("should never get here\n");
  return VM_FAILED;

fail:
  PMAP_DEBUG(("vm_pmap_lookup: virt %p\n", virt));
  return VM_FAILED;
}

/* modify the flags of a virtual to physical mapping
	 Also the possible entry in the TLB has to be updated (tlbp).
 */
int vm_pmap_change_page_flags(pmap_t pmap, address_t virt, int flags)
{
  /* As noted in vm_pmap_lookup the function can only downgrade entries.
  	 Upgrades are done on a case by case evaluation.
  	 XXX probably we should also store the permission flags in the vtable
  	 The Problem is that I store access info in different places so it can
  	 be hard to implement copy on write.
   */
  /* STEP 1: find vtable entry */
  /* STEP 2: check ptable entry */
  /* STEP 3: downgrade if needed */
  /* STEP 4: update TLB if needed */
  return VM_NIMP;
}

/* the same as befor but this time the complete region is changed */
int vm_pmap_change_region_flags(pmap_t pmap, address_t virt_start, address_t virt_end, int flags)
{
  /* cycle through the region and call vm_pmap_change_page_flags for each page */
  return VM_NIMP;
}

/* copy a page, this is should be the fastest possible function
	 NOTE: we are using vm_page struct because on the IXP the pmap layer needs
	 to map both pages into his protection domain before the copy can start and
	 afterwards the pages have to be removed again.
	 On the mips we reserve the first 0x10000 bytes for this pmap handling.
 */
int vm_pmap_page_copy(pmap_t pmap, struct vm_page* src, struct vm_page* dest)
{
  address_t *s;
  address_t *d;
  /* XXX this may break if we use more than 512 MB of physical RAM. */

  d = (address_t)PHYS_TO_K1SEG(PAGETOADDR(dest->pagenr));
  s = (address_t)PHYS_TO_K1SEG(PAGETOADDR(src->pagenr));

  ASSERT(ISA_K1SEG(d) && ISA_K1SEG(s));

  /* copy the page with the std. byteCopy, somewhen we should write a asm byteCopy */
  byteCopy(d, s, PAGESIZE);
  /* page copyed, we're done. */

  printf("vm_pmap_page_copy: src %#lx dest %#lx\n", src->pagenr, dest->pagenr);
  return VM_OK;
}

/* zeroout a page, again this should be the fastest possible function */
int vm_pmap_page_zero(pmap_t pmap, struct vm_page* src)
{
  address_t *s;
  /* XXX this may break if we use more than 512 MB of physical RAM. */

  s = (address_t)PHYS_TO_K1SEG(PAGETOADDR(src->pagenr));

  ASSERT(ISA_K1SEG(s));

  /* zero the page with the std. zeroOut, somewhen we should write a asm zeroOut */
  zeroOut(s, PAGESIZE);
  /* page zeroed, we're done. */

  //-CJ	printf("vm_pmap_page_zero: src %#lx\n", src->pagenr);
  return VM_OK;
}

static char* print_state(Register tlblo, char* b5)
{
  char *s = b5;

  if (PMAP_ENTRY_ISSET(tlblo, PMAP_PENTRY_GLOBAL))
    *s++ = 'g';
  else
    *s++ = ' ';
  if (PMAP_ENTRY_ISSET(tlblo, PMAP_PENTRY_VALID))
    *s++ = 'v';
  else
    *s++ = ' ';
  if (PMAP_ENTRY_ISSET(tlblo, PMAP_PENTRY_DIRTY))
    *s++ = 'd';
  else
    *s++ = ' ';
  if (PMAP_ENTRY_ISSET(tlblo, PMAP_PENTRY_NONCACHE))
    *s++ = ' ';
  else
    *s++ = 'c';
  *s = '\0';
  return b5;
}

const static char levelstr[] = "*+++++++++";

void vm_pmap_print_entry(pmap_ventry_t* v)
{
  char status[5];
  pmap_pentry_t* p;

  p = v->pentry;
  printf("   %p %5s -> %p (%lu) %s\n",
         PMAP_VENTRY_VIRT(v->tlbhi),
         PMAP_ENTRY_ISSET(v->tlbhi, PMAP_VENTRY_WRITE)?"write":"",
         (address_t)PMAP_PENTRY_PHYS(p->tlblo), p->count,
         print_state(p->tlblo, status));
}

void vm_pmap_print(pmap_t pmap)
{
  char status[5];
  int i;
  int size;
  int level;
  pmap_ventry_t* v;
  pmap_ventry_t* t;
  pmap_pentry_t* p;

  t = vm->pmap->vtable;
  printf("vm pmap mappings of PD %li:\n====================================\n",
         pmap->pdom->pdId);
  for ( i = 0, size = vm->pmap->vhashsize; i < size; i++ )
  {
    level = 1;
    v = t;
    do
    {
      if (!PMAP_VENTRY_FREE(v->tlbhi) && v->vm_pmap == pmap)
      {
        p = v->pentry;
        printf("   %p %5s -> %p (%lu) %s %s\n",
               PMAP_VENTRY_VIRT(v->tlbhi),
               PMAP_ENTRY_ISSET(v->tlbhi, PMAP_VENTRY_WRITE)?"write":"",
               (address_t)PMAP_PENTRY_PHYS(p->tlblo), p->count,
               print_state(p->tlblo, status), (char*)levelstr + 10 - (level>10?10:level));
      }
      v = v->next;
    }
    while( v != NULL );
    t++;
  }
}


void vm_pmap_print_all(void)
{
  char status[5];
  int i;
  int size;
  int level;
  pmap_ventry_t* v;
  pmap_ventry_t* t;
  pmap_pentry_t* p;

  t = vm->pmap->vtable;
  printf("vm pmap table dump:\n====================================\n");
  for ( i = 0, size = vm->pmap->vhashsize; i < size; i++ )
  {
    level = 1;
    v = t;
    do
    {
      if (! PMAP_VENTRY_FREE(v->tlbhi) )
      {
        p = v->pentry;
        printf("  PD %li: %p %5s -> %p (%lu) %s %s\n",
               v->vm_pmap->pdom->pdId, PMAP_VENTRY_VIRT(v->tlbhi),
               PMAP_ENTRY_ISSET(v->tlbhi, PMAP_VENTRY_WRITE)?"write":"",
               (address_t)PMAP_PENTRY_PHYS(p->tlblo), p->count,
               print_state(p->tlblo, status), (char*)levelstr + 10 - (level>10?10:level));
      }
      v = v->next;
      level++;
    }
    while( v != NULL );
    t++;
  }
}
