/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * Copyright 1991-1998 by Apple Computer, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * MkLinux
 */

#include <mach_debug.h>
#include <mach_kdb.h>
#include <debug.h>
#include <mach_kgdb.h>
#include <cpus.h>

#include <mach/vm_types.h>
#include <mach/vm_param.h>
#include <mach/thread_status.h>
#include <kern/misc_protos.h>
#include <kern/assert.h>
#include <kern/cpu_number.h>

#include <ppc/proc_reg.h>
#include <ppc/boot.h>
#include <ppc/misc_protos.h>
#include <ppc/pmap.h>
#include <ppc/pmap_internals.h>
#include <ppc/mem.h>
#include <ppc/exception.h>
#include <ppc/gdb_defs.h>
#include <ppc/POWERMAC/video_board.h>
#include <ppc/POWERMAC/video_pdm.h>
#include <ppc/POWERMAC/device_tree.h>
#if NCPUS > 1
#include <ppc/mp.h>
#include <ppc/POWERMAC/mp/mp.h>
#endif /* NCPUS > 1 */

/* External references */

#if	MACH_KDB
extern vm_offset_t	kern_sym_start;
extern vm_size_t	kern_sym_size;
extern const char *getenv(const char *name);
#endif	/* MACH_KDB */
extern unsigned int intstack[];	/* declared in start.s */
extern unsigned int intstack_top_ss;	/* declared in start.s */
#if	MACH_KGDB
extern unsigned int gdbstackptr;	/* declared in start.s */
extern unsigned int gdbstack_top_ss;	/* declared in start.s */
#endif	/* MACH_KGDB */

/* Stuff declared in kern/bootstrap.c which we may need to initialise */

extern vm_offset_t     boot_start;
extern vm_size_t       boot_size;
extern vm_offset_t     boot_region_desc;
extern vm_size_t       boot_region_count;
extern int             boot_thread_state_flavor;
extern thread_state_t  boot_thread_state;
extern unsigned int    boot_thread_state_count;

/* Trap handling function prototypes */

extern void thandler(void);	/* trap handler */
extern void ihandler(void);	/* interrupt handler */
extern void shandler(void);	/* syscall handler */
extern void gdbhandler(void);	/* debugger handler */
extern void fpu_switch(void);	/* fp handler */

/* definitions */

struct ppc_thread_state boot_task_thread_state;

void (*exception_handlers[])(void) = {
	thandler,	/* 0x000   INVALID EXCEPTION */
	thandler,	/* 0x100   System reset */
	thandler,	/* 0x200   Machine check */
	thandler,	/* 0x300   Data access */
	thandler,	/* 0x400   Instruction access */
	ihandler,	/* 0x500   External interrupt */
	thandler,	/* 0x600   Alignment */
	thandler,	/* 0x700   Program - fp exc, ill/priv instr, trap */
	fpu_switch,	/* 0x800   Floating point disabled */
	ihandler,	/* 0x900   Decrementer */
	thandler,	/* 0xA00   I/O controller interface */
	thandler,	/* 0xB00   INVALID EXCEPTION */
	shandler,	/* 0xC00   System call exception */
	thandler,	/* 0xD00   Trace */
	thandler,	/* 0xE00   FP assist */
	thandler,	/* 0xF00   INVALID EXCEPTION */
	thandler,	/* 0x1000  INVALID EXCEPTION */
	thandler,	/* 0x1100  INVALID EXCEPTION */
	thandler,	/* 0x1200  INVALID EXCEPTION */
	thandler,	/* 0x1300  instruction breakpoint */
	thandler,	/* 0x1400  system management */
	thandler,	/* 0x1500  INVALID EXCEPTION */
	thandler,	/* 0x1600  INVALID EXCEPTION */
	thandler,	/* 0x1700  INVALID EXCEPTION */
	thandler,	/* 0x1800  INVALID EXCEPTION */
	thandler,	/* 0x1900  INVALID EXCEPTION */
	thandler,	/* 0x1A00  INVALID EXCEPTION */
	thandler,	/* 0x1B00  INVALID EXCEPTION */
	thandler,	/* 0x1C00  INVALID EXCEPTION */
	thandler,	/* 0x1D00  INVALID EXCEPTION */
	thandler,	/* 0x1E00  INVALID EXCEPTION */
	thandler,	/* 0x1F00  INVALID EXCEPTION */
	thandler,	/* 0x2000  Run Mode/Trace */
	ihandler	/* 0x2100  Signal processor */
};

/* per_proc_info is accessed with VM switched off via sprg0 */
/* Note that we always get enough space for an extra cache line.  That is because
   for performance, we need to align the struct on a cache boundary.  With the
   extra space, we can shift the array up to 31 bytes to align */


/*	 struct per_proc_info per_proc_info[NCPUS];		*/		/* Unaligned here */
unsigned char per_proc_area[NCPUS*sizeof(struct per_proc_info)+32]; 
struct per_proc_info *per_proc_info; 

vm_size_t mem_size;  /* Size of actual physical memory present in bytes */

mem_region_t pmap_mem_regions[PMAP_MEM_REGION_MAX];
int	 pmap_mem_regions_count = 0;	/* No non-contiguous memory regions */

mem_region_t free_regions[FREE_REGION_MAX];
int	     free_regions_count;

vm_offset_t virtual_avail, virtual_end;

extern unsigned long etext;
extern unsigned long _ExceptionVectorsStart;
extern unsigned long _ExceptionVectorsEnd;

#if 1 /* TODO NMGS - vm_map_steal_memory shouldn't use these - remove */
vm_offset_t avail_start;
vm_offset_t avail_end;
#endif 
unsigned int avail_remaining = 0;
vm_offset_t first_avail;

#if	NCPUS > 1
void initPerProc(void);
vm_offset_t	interrupt_stack[NCPUS];
#endif	/* NCPUS > 1 */

/* This is the first C function to be called. It is called with VM
 * switched ON, with the bottom 2M of memory mapped into KERNELBASE_TEXT
 * via BAT0, and the region in 2-4M mapped 1-1 (KERNELBASE_DATA) via BAT1
 *
 * The IO space is mapped 1-1 either via a segment register or via
 * BAT2, depending upon the processor type.
 *
 * printf already works
 * 
 * First initialisation of memory.
 *  - zero bss,
 *  - invalidate some seg regs,
 *  - set up hash tables
 */

void ppc_vm_init(unsigned int memory_size, boot_args *args)
{
	unsigned int htabmask;
	unsigned int i;
	vm_size_t  addr;
	int boot_task_end_offset;
#if	NCPUS > 1
	const char *cpus;
#endif	/* NCPUS > 1 */

	printf("mem_size = %d M\n",memory_size / (1024 * 1024));

#define IMAC_SUPPORT
#ifdef IMAC_SUPPORT
	if (powermac_info.class == POWERMAC_CLASS_PCI) {

		/* All PCI macs have contiguous RAM, so force the
		   issue (for machines with bogus RAM bank tables) */
		args->PhysicalDRAM[0].base = 0;
		args->PhysicalDRAM[0].size = memory_size;

		/* now flush the bogus information in other
		   RAM bank table entries */
		for (i = 1; i<kMaxDRAMBanks; i++)
			args->PhysicalDRAM[i].size = 0;

	} else {
#else
	{
#endif

	 /* Stitch valid memory regions together - they may be contiguous
	  * even though they're not already glued together
	  */
	    addr = 0;	/* temp use as pointer to previous memory region... */
	    for (i = 1; i < kMaxDRAMBanks; i++) {
	  	if (args->PhysicalDRAM[i].size == 0)
			continue;
	  	if (args->PhysicalDRAM[i].base ==
		    args->PhysicalDRAM[addr].base +
		    args->PhysicalDRAM[addr].size) {
		  	/* The two regions are contigous, join them */
		  printf("region 0x%08x size 0x%08x joining region "
			 "0x%08x size 0x%08x\n",
			 args->PhysicalDRAM[addr].base,
			 args->PhysicalDRAM[addr].size,
			 args->PhysicalDRAM[i].base,
			 args->PhysicalDRAM[i].size);
			args->PhysicalDRAM[addr].size +=
			  	args->PhysicalDRAM[i].size;
			args->PhysicalDRAM[i].size = 0;
			/* "last" region remains this new bigger region */
		} else {
		  	/* move "last" region up to this one */
			addr = i;
		}
	    }
	}

	/* Go through the list of memory regions passed in via the args
	 * and copy valid entries into the pmap_mem_regions table, adding
	 * further calculated entries.
	 */
	
	   pmap_mem_regions_count = 0;
	   addr = 0;	/* Will use to total memory found so far */
	   for (i = 0; (addr < memory_size) && (i < kMaxDRAMBanks); i++) {
		if (args->PhysicalDRAM[i].size == 0)
			continue;

		/* The following should only happen if memory size has
		   been artificially reduced with -m */
		if (addr + args->PhysicalDRAM[i].size > memory_size)
			args->PhysicalDRAM[i].size = memory_size - addr;

		/* We've found a region, tally memory */

		pmap_mem_regions[pmap_mem_regions_count].start =
			args->PhysicalDRAM[i].base;
		pmap_mem_regions[pmap_mem_regions_count].end =
			args->PhysicalDRAM[i].base +
			args->PhysicalDRAM[i].size;

		/* Regions must be provided in ascending order */
		assert ((pmap_mem_regions_count == 0) ||
			pmap_mem_regions[pmap_mem_regions_count].start >
			pmap_mem_regions[pmap_mem_regions_count-1].start);

#ifdef IMAC_TEST
		printf("pmap[Table]: added region from %x to %x with size %x", 
			pmap_mem_regions[pmap_mem_regions_count].start,
			pmap_mem_regions[pmap_mem_regions_count].end,
			(pmap_mem_regions[pmap_mem_regions_count].end -
			 pmap_mem_regions[pmap_mem_regions_count].start));
#endif

		if (pmap_mem_regions_count > 0) {		
			/* we add on any pages not in the first memory
			 * region to the avail_remaining count. The first
			 * memory region is used for mapping everything for
			 * bootup and is taken care of specially.
			 */
			avail_remaining +=
				args->PhysicalDRAM[i].size / PPC_PGBYTES;
		}
		
		/* Keep track of how much memory we've found */

		addr += args->PhysicalDRAM[i].size;

		/* incremement number of regions found */
		pmap_mem_regions_count++;
	   }
	if (mem_size != addr) {
		printf("mem_size set to 0x%x\n", mem_size);
		mem_size = addr; /* Set up global variable */
	}
	
	/* Initialise the pmap system, using space above `first_avail'
	 * for the necessary data structures.
         * NOTE : assume that we'll have enough space mapped in already
         */

	/* Set up the various globals describing memory usage */

	/* A small table of free physical memory left behind by the
	 * early kernel initialisation from the first contiguous memory
	 * region. We pick up this memory later via the free_regions
	 * array.
	 *
	 * Free memory can be found between end of copied exception
	 * vectors and kernel text, and
	 * between the end of kernel text and the start of kernel data.
	 */

	free_regions[free_regions_count].start =
	  	round_page((unsigned int)&_ExceptionVectorsEnd -
			   (unsigned int)&_ExceptionVectorsStart);

	/* If we are on a PDM machine memory at 1M might be used
	 * for video. TODO NMGS call video driver to do this
	 * somehow
	 */
	if (IS_VIDEO_MEMORY(VPDM_PHYSADDR)) {

		/* cut this region short at the start of video */

		free_regions[free_regions_count].end   = VPDM_PHYSADDR;

		avail_remaining += (free_regions[free_regions_count].end - 
				    free_regions[free_regions_count].start) /
			PPC_PGBYTES;
		free_regions_count++;

		/* next region now starts after end of frame buffer */
		free_regions[free_regions_count].start =
			round_page(VPDM_PHYSADDR + VPDM_FRAMEBUF_MAX_SIZE);

	}

	free_regions[free_regions_count].end   = KERNELBASE_TEXT;

        avail_remaining += (free_regions[free_regions_count].end - 
                            free_regions[free_regions_count].start) /
                                    PPC_PGBYTES;

        free_regions_count++;


	free_regions[free_regions_count].start = round_page(&etext);
	free_regions[free_regions_count].end   = KERNELBASE_DATA;

        avail_remaining += (free_regions[free_regions_count].end - 
                            free_regions[free_regions_count].start) /
                                    PPC_PGBYTES;

        free_regions_count++;

	/* For PowerMac, first_avail is set to above the bootstrap task.
         * TODO NMGS - different screen modes - might free mem?
         */

	first_avail = round_page(args->first_avail);

#if	NCPUS > 1
	/*
	 * Must Allocate interrupt stacks before kdb is called and also
	 * before vm is initialized. Must find out number of cpus first.
	 */
	/*
	 * Get number of cpus to boot, passed as an optional argument
	 * boot: mach [-sah#]	# from 0 to 9 is the number of cpus to boot
	 */
	if (wncpu < 0) {
		/* If already > 0, then was command line arg, override env */
		if ((cpus = getenv("cpus")) != NULL) {
			/* only a single digit for now */
			if ((*cpus > '0') && (*cpus <= '9'))
				wncpu = *cpus - '0';
		} else {
			wncpu = NCPUS;
		}
	}
#endif	/* NCPUS > 1 */

#undef DEBUG
#define DEBUG 1

	pmap_bootstrap(memory_size,&first_avail);

	/* map in the exception vectors */
	{
		extern unsigned long exception_entry;
		extern unsigned long exception_end;

#if DEBUG
		printf("Mapping exception entry/exit 0x%x to 0x%x size 0x%x\n",
		       trunc_page(exception_entry),
		       trunc_page(exception_entry),
		       round_page(exception_end));
#endif /* DEBUG */
		pmap_map(trunc_page(exception_entry),
			 trunc_page(exception_entry),
			 round_page(exception_end),
			 VM_PROT_READ|VM_PROT_EXECUTE);
	}
	/*
	 * map the kernel text, data and bss. Don't forget other regions too
	 */
	for (i = 0; i < args->kern_info.region_count; i++) {
#if	MACH_KDB
		if (args->kern_info.regions[i].prot == VM_PROT_NONE &&
		    i == args->kern_info.region_count - 1) {
			/* assume that's the kernel symbol table */
			kern_sym_start = args->kern_info.regions[i].addr;
			kern_sym_size = args->kern_info.regions[i].size;
			printf("kernel symbol table at 0x%x size 0x%x\n",
			       kern_sym_start, kern_sym_size);
			args->kern_info.regions[i].prot |=
				(VM_PROT_WRITE|VM_PROT_READ);
		}
#endif	/* MACH_KDB */
#if DEBUG
		printf("kernel: mapping virt 0x%08x to phys 0x%08x size 0x%x, prot=0x%b\n",
		       ppc_trunc_page(args->kern_info.regions[i].addr),
		       ppc_trunc_page(args->kern_info.base_addr + 
				      args->kern_info.regions[i].offset),
		       ppc_round_page(args->kern_info.base_addr + 
				      args->kern_info.regions[i].size),
		       args->kern_info.regions[i].prot,
		       "\x10\1READ\2WRITE\3EXEC");
#endif /* DEBUG */
		(void)pmap_map(ppc_trunc_page(args->kern_info.regions[i].addr),
			       ppc_trunc_page(args->kern_info.base_addr + 
				      args->kern_info.regions[i].offset),
			       ppc_round_page(args->kern_info.base_addr + 
				      args->kern_info.regions[i].offset +
				      args->kern_info.regions[i].size),
			 args->kern_info.regions[i].prot);
	}
	boot_region_count = args->task_info.region_count;
	boot_size = 0;
	boot_task_end_offset = 0;
	/* Map bootstrap task pages 1-1 so that user_bootstrap can find it */
	for (i = 0; i < boot_region_count; i++) {
		if (args->task_info.regions[i].mapped) {
			/* kernel requires everything page aligned */
#if DEBUG
			printf("bootstrap: mapping virt 0x%08x to phys 0x%08x end 0x%x, prot=0x%b\n",
				 ppc_trunc_page(args->task_info.base_addr + 
					args->task_info.regions[i].offset),
				 ppc_trunc_page(args->task_info.base_addr + 
					args->task_info.regions[i].offset),
				 ppc_round_page(args->task_info.base_addr + 
					args->task_info.regions[i].offset +
					args->task_info.regions[i].size),
				 args->task_info.regions[i].prot,
				 "\x10\1READ\2WRITE\3EXEC");
#endif /* DEBUG */

			(void)pmap_map(
				  ppc_trunc_page(args->task_info.base_addr + 
				      args->task_info.regions[i].offset),
			          ppc_trunc_page(args->task_info.base_addr + 
				      args->task_info.regions[i].offset),
			          ppc_round_page(args->task_info.base_addr +
				      args->task_info.regions[i].offset +
				      args->task_info.regions[i].size),
			          args->task_info.regions[i].prot);

			/* Count the size of mapped space */
			boot_size += args->task_info.regions[i].size;

			/* There may be an overlapping physical page
			 * mapped to two different virtual addresses
			 */
			if (boot_task_end_offset >
			    args->task_info.regions[i].offset) {
				boot_size -= boot_task_end_offset - 
					args->task_info.regions[i].offset;
#if DEBUG
				printf("WARNING - bootstrap overlaps regions\n");
#endif /* DEBUG */
			}

			boot_task_end_offset =
				args->task_info.regions[i].offset +
				args->task_info.regions[i].size;
		}
	}

	if (boot_region_count) {

		/* Add a new region to the bootstrap task for it's stack */
		args->task_info.regions[boot_region_count].addr =
			BOOT_STACK_BASE;
		args->task_info.regions[boot_region_count].size =
			BOOT_STACK_SIZE;
		args->task_info.regions[boot_region_count].mapped = FALSE;
		boot_region_count++;
		
		boot_start        = args->task_info.base_addr;
		boot_region_desc  = (vm_offset_t) args->task_info.regions;
		/* TODO NMGS need to put param info onto top of boot stack */
		boot_task_thread_state.r1   = BOOT_STACK_PTR-0x100;
		boot_task_thread_state.srr0 = args->task_info.entry;
		boot_task_thread_state.srr1 =
			MSR_MARK_SYSCALL(MSR_EXPORT_MASK_SET);
		
		boot_thread_state_flavor = PPC_THREAD_STATE;
		boot_thread_state_count  = PPC_THREAD_STATE_COUNT;
		boot_thread_state        =
			(thread_state_t)&boot_task_thread_state;
	}

	/* Map bootstrap argument structure as data */
	(void) pmap_map((vm_offset_t)args,
			(vm_offset_t)args-KERNELBASE_DATA_OFFSET,
			((vm_offset_t)args->first_avail - 1) -
				KERNELBASE_DATA_OFFSET,
			VM_PROT_READ|VM_PROT_WRITE);

	/* Map interrupt stack red-zone */
	addr = trunc_page((vm_offset_t) &intstack - PPC_PGBYTES);
	(void) pmap_map(addr,
			addr - KERNELBASE_DATA_OFFSET,
			addr - KERNELBASE_DATA_OFFSET + PPC_PGBYTES-1,
			VM_PROT_READ);

	/* Map motherboard video memory via PTEs before removing BAT */

	if (IS_VIDEO_MEMORY(VPDM_PHYSADDR)) {
		(void) pmap_map_bd(VPDM_BASEADDR,
				   VPDM_PHYSADDR,
				   round_page(VPDM_PHYSADDR +
					      VPDM_FRAMEBUF_MAX_SIZE),
				   VM_PROT_READ | VM_PROT_WRITE);

		/* KAM 990104 - unmap BAT3 in case we're on a 750 and don't
		   do it below */
		mtdbatu(3,BAT_INVALID); mtdbatl(3,BAT_INVALID);
	}
	/* Set up per_proc info */
#if 0
	per_proc_info[0].cpu_number = 0;
#endif
	per_proc_info[cpu_number()].istackptr = 0; /* we're on the intr stack */
	per_proc_info[cpu_number()].intstack_top_ss = intstack_top_ss;
#if	MACH_KGDB
	per_proc_info[cpu_number()].gdbstackptr = gdbstackptr;
	per_proc_info[cpu_number()].gdbstack_top_ss = gdbstack_top_ss;
#endif	/* MACH_KGDB */
	per_proc_info[cpu_number()].phys_exception_handlers =
		kvtophys((vm_offset_t)&exception_handlers);
	per_proc_info[cpu_number()].virt_per_proc_info = (unsigned int)
		&per_proc_info[cpu_number()];
	per_proc_info[cpu_number()].active_kloaded = (unsigned int)
		&active_kloaded[cpu_number()];
	per_proc_info[cpu_number()].cpu_data = (unsigned int)
		&cpu_data[cpu_number()];
	per_proc_info[cpu_number()].active_stacks = (unsigned int)
		&active_stacks[cpu_number()];
	per_proc_info[cpu_number()].need_ast = (unsigned int)
		&need_ast[cpu_number()];
	per_proc_info[cpu_number()].fpu_pcb = 0;

	mtsprg(0, ((unsigned int)kvtophys((vm_offset_t)&per_proc_info[cpu_number()])));

	/* r2 points to our per_proc structure whilst we're in the kernel */
	__asm__ ("mr 2, %0" : : "r" (&per_proc_info[cpu_number()]));


#if MACH_KGDB
	kgdb_kernel_in_pmap = TRUE;
#endif /* MACH_KGDB */

#if DEBUG
	for (i=0 ; i < free_regions_count; i++) {
		printf("Free region start 0x%08x end 0x%08x\n",
		       free_regions[i].start,free_regions[i].end);
	}
#endif


	sync();isync();

	if (PROCESSOR_VERSION != PROCESSOR_VERSION_601) {
/*		mtdbatu(3,BAT_INVALID); mtdbatl(3,BAT_INVALID); TODO NMGS */
/* 		mtdbatu(2,BAT_INVALID); mtdbatl(2,BAT_INVALID); TODO NMGS */
		mtdbatu(1,BAT_INVALID); mtdbatl(1,BAT_INVALID);
		mtdbatu(0,BAT_INVALID); mtdbatl(0,BAT_INVALID);

		/* Clear instr BATs too on those processors that have them */
		mtibatu(3,BAT_INVALID); mtibatl(3,BAT_INVALID);
 		mtibatu(2,BAT_INVALID); mtibatl(2,BAT_INVALID);
		mtibatu(1,BAT_INVALID); mtibatl(1,BAT_INVALID);
		mtibatu(0,BAT_INVALID); mtibatl(0,BAT_INVALID);

		/* Kernel text+rodata is at 2-4M */

		/* use dbat 0 to map 2-4M 1-1 read-only for kernel only */
		mtdbatl(0, KERNELBASE_TEXT | PTE_WIMG_DEFAULT << 3 | 1);
		mtdbatu(0, KERNELBASE_TEXT | 0xf << 2 | 2);

		/* use ibat 0 to map 2-4M 1-1 executable for kernel only */
		mtibatl(0, KERNELBASE_TEXT | PTE_WIMG_DEFAULT << 3 | 1);
		mtibatu(0, KERNELBASE_TEXT | 0xf << 2 | 2);

		/* Kernel r/w data is at 4M and up */

		/* Must only map to less than first_avail
		 * since VM addresses above that may not be 1-1
		 */
		addr = 128 * 1024;
		i = 0;
		while (KERNELBASE_DATA + (addr * 2) < first_avail) {
			i = (i * 2) +1;
			addr = addr * 2;
		}

		if ( i > 31 ) {
			printf("Clamping BAT size to 4M\n");
			i = 31;
		}

		/* use dbat 1 to map 4M-XXX 1-1 read-write for kernel only */
		mtdbatl(1, KERNELBASE_DATA | (PTE_WIMG_DEFAULT << 3) | 2);
		mtdbatu(1, KERNELBASE_DATA | (i << 2) | 2);

		/* MP plugin has relocatable code stored in data segment */
		/* use ibat 1 to map 4M-XXX 1-1 executable for kernel only */
		mtibatl(1, KERNELBASE_DATA | (PTE_WIMG_DEFAULT << 3) | 2);
		mtibatu(1, KERNELBASE_DATA | (i << 2) | 2);
	} else {
		mtibatl(3,BAT_INVALID);
		mtibatl(2,BAT_INVALID);
		mtibatl(1,BAT_INVALID);
		mtibatl(0,BAT_INVALID);
	}
	sync();isync();
	{
		int i;
		__asm__ ("mr %0, 2" : "=r" (i));
		assert(i = (int)&per_proc_info[cpu_number()]);
	}
}

