/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe is free software; you can redistribute it and/or modify            *
*   it under the terms of the GNU General Public License as published by      *
*   the Free Software Foundation; either version 2 of the License, or         *
*   (at your option) any later version.                                       *
*                                                                             *
*   Nprobe is distributed in the hope that it will be useful,                 *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*   GNU General Public License for more details.                              *
*                                                                             *
*   You should have received a copy of the GNU General Public License         *
*   along with Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/


#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/swap.h>
#include <linux/smp_lock.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>

#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm/tlb.h>

/* Urgg... we give userspace access to kernel memory from here...
   Truly horrible. */

static void hack_pte_page(unsigned long *page, int rw)
{
  int x;

  for (x = 0; x < 1024; x++) {
    if (page[x] & _PAGE_PRESENT)
      page[x] |= _PAGE_USER;
  }
}

int hack_page_range(unsigned long address, unsigned long size, int rw)
{
  int error = 0;
  pgd_t * dir;
  unsigned long end = address + size;
  //  struct mm_struct *mm = current->mm;
  struct mm_struct *mm = &init_mm;

  dir = pgd_offset_k(address);

  flush_cache_range(mm, address, end);

  if (address >= end)
    BUG();
  
  spin_lock(&mm->page_table_lock);
  do
    {
      unsigned long entry =  *(unsigned long *)dir; 


      if ( (entry & _PAGE_PSE) && (entry & _PAGE_PRESENT) )
	{
	  // this is a 4MB page
	  
	  printk("NPROBE: found a 4MB page at %08lx. entry %08lx\n",
		 address, entry );

	  entry |= _PAGE_USER;
	  //if( !rw) entry &= ~_PAGE_RW;
	  
	  *(unsigned long *)dir = entry;
	}
      else
	{   // 4KB page
	  hack_pte_page(__va(entry & ~4095), rw);
	}

      if (error)
	break;
      address = (address + PGDIR_SIZE) & PGDIR_MASK;
      dir++;

    }
  while (address && (address < end));

  spin_unlock(&mm->page_table_lock);

#define X86_CR4_PGE             0x0080  /* enable global pages */
  {
                unsigned int tmpreg,tmpreg2;                            
                                                                        
                __asm__ __volatile__(                                   
                        "movl %%cr4, %0;  # read CR4     \n"            
			"movl %0, %1;     # copy CR4 \n"		
			"andl %2, %1;     # clear PGE \n"		
                        "movl %1, %%cr4;  # turn off PGE     \n"        
                        "movl %%cr3, %1;  # flush TLB        \n"        
                        "movl %1, %%cr3;                     \n"        
                        "movl %0, %%cr4;  # turn PGE back on \n"        
                        : "=&r" (tmpreg), "=&r" (tmpreg2)               
                        : "r" (~X86_CR4_PGE)                             
                        : "memory");                                    
   }

  return error;
}

