[BACK]Return to machdep.c CVS log [TXT][DIR] Up to [local] / sys / arch / sgi / sgi

File: [local] / sys / arch / sgi / sgi / machdep.c (download)

Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:07:50 2008 UTC (16 years, 3 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD
Changes since 1.1: +0 -0 lines

Import of OpenBSD 4.2 release kernel tree with initial code to support 
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO

Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)

/*	$OpenBSD: machdep.c,v 1.47 2007/07/18 20:05:25 miod Exp $ */

/*
 * Copyright (c) 2003-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/msgbuf.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/user.h>
#include <sys/exec.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/exec_elf.h>
#include <sys/extent.h>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#ifdef SYSVSEM
#include <sys/sem.h>
#endif
#ifdef SYSVMSG
#include <sys/msg.h>
#endif

#include <uvm/uvm_extern.h>

#include <machine/db_machdep.h>
#include <ddb/db_interface.h>

#include <machine/pte.h>
#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/pio.h>
#include <machine/psl.h>
#include <machine/autoconf.h>
#include <machine/memconf.h>
#include <machine/regnum.h>
#if defined(TGT_ORIGIN200) || defined(TGT_ORIGIN2000)
#include <machine/mnode.h>
#endif

#include <mips64/rm7000.h>

#include <dev/cons.h>

#include <mips64/arcbios.h>
#include <mips64/archtype.h>
#include <machine/bus.h>

#include <sgi/localbus/crimebus.h>
#include <sgi/localbus/macebus.h>
#if defined(TGT_ORIGIN200) || defined(TGT_ORIGIN2000)
#include <sgi/localbus/xbowmux.h>
#endif

extern struct consdev *cn_tab;
extern char kernel_text[];
extern int makebootdev(const char *, int);
extern void stacktrace(void);

#ifdef DEBUG
void dump_tlb(void);
#endif

/* the following is used externally (sysctl_hw) */
char	machine[] = MACHINE;		/* machine "architecture" */
char	cpu_model[30];

/*
 * Declare these as initialized data so we can patch them.
 */
#ifndef	BUFCACHEPERCENT
#define	BUFCACHEPERCENT	5	/* Can be changed in config */
#endif
#ifndef	BUFPAGES
#define BUFPAGES 0		/* Can be changed in config */
#endif

int	bufpages = BUFPAGES;
int	bufcachepercent = BUFCACHEPERCENT;

vm_map_t exec_map;
vm_map_t phys_map;

int	extent_malloc_flags = 0;

caddr_t	msgbufbase;

int	physmem;		/* max supported memory, changes to actual */
int	rsvdmem;		/* reserved memory not usable */
int	ncpu = 1;		/* At least one cpu in the system */
struct	user *proc0paddr;
struct	user *curprocpaddr;
int	console_ok;		/* set when console initialized */
int	bootdriveoffs = 0;

int32_t *environment;
struct sys_rec sys_config;


/* ddb symbol init stuff */
caddr_t	ssym;
caddr_t	esym;
caddr_t	ekern;

struct phys_mem_desc mem_layout[MAXMEMSEGS];

void crime_configure_memory(void);

caddr_t mips_init(int, void *);
void initcpu(void);
void dumpsys(void);
void dumpconf(void);
caddr_t allocsys(caddr_t);

void db_command_loop(void);

static void dobootopts(int, void *);
static int atoi(const char *, int, const char **);

#if BYTE_ORDER == BIG_ENDIAN
int	my_endian = 1;
#else
int	my_endian = 0;
#endif

#if defined(TGT_O2)
void
crime_configure_memory(void)
{
	struct phys_mem_desc *m;
	volatile u_int64_t *bank_ctrl;
	paddr_t addr;
	psize_t size;
	u_int32_t first_page, last_page;
	int bank, i;

	bank_ctrl = (void *)PHYS_TO_KSEG1(CRIMEBUS_BASE + CRIME_MEM_BANK0_CONTROL);
	for (bank = 0; bank < CRIME_MAX_BANKS; bank++) {
		addr = (bank_ctrl[bank] & CRIME_MEM_BANK_ADDR) << 25;
		size = (bank_ctrl[bank] & CRIME_MEM_BANK_128MB) ? 128 : 32;
#ifdef DEBUG
		bios_printf("crime: bank %d contains %ld MB at 0x%lx\n",
		    bank, size, addr);
#endif

		/*
		 * Do not report memory regions below 256MB, since
		 * arcbios will do. Moreover, empty banks are reported
		 * at address zero.
		 */
		if (addr < 256 * 1024 * 1024)
			continue;

		addr += 1024 * 1024 * 1024;
		size *= 1024 * 1024;
		first_page = atop(addr);
		last_page = atop(addr + size);

		/*
		 * Try to coalesce with other memory segments if banks
		 * are contiguous.
		 */
		m = NULL;
		for (i = 0; i < MAXMEMSEGS; i++) {
			if (mem_layout[i].mem_last_page == 0) {
				if (m == NULL)
					m = &mem_layout[i];
			} else if (last_page == mem_layout[i].mem_first_page) {
				m = &mem_layout[i];
				m->mem_first_page = first_page;
			} else if (mem_layout[i].mem_last_page == first_page) {
				m = &mem_layout[i];
				m->mem_last_page = last_page;
			}
		}
		if (m != NULL && m->mem_last_page == 0) {
			m->mem_first_page = first_page;
			m->mem_last_page = last_page;
		}
		if (m != NULL)
			physmem += atop(size);
	}

#ifdef DEBUG
	for (i = 0; i < MAXMEMSEGS; i++)
		if (mem_layout[i].mem_first_page)
			bios_printf("MEM %d, 0x%x to  0x%x\n",i,
				ptoa(mem_layout[i].mem_first_page),
				ptoa(mem_layout[i].mem_last_page));
#endif
}
#endif

/*
 * Do all the stuff that locore normally does before calling main().
 * Reset mapping and set up mapping to hardware and init "wired" reg.
 */

caddr_t
mips_init(int argc, void *argv)
{
	char *cp;
	int i;
	caddr_t sd;
	extern char start[], edata[], end[];
	extern char tlb_miss_tramp[], e_tlb_miss_tramp[];
	extern char xtlb_miss_tramp[], e_xtlb_miss_tramp[];
	extern char exception[], e_exception[];

	/*
	 * Make sure we can access the extended address space.
	 * Note that r10k and later do not allow XUSEG accesses
	 * from kernel mode unless SR_UX is set.
	 */
	setsr(getsr() | SR_KX | SR_UX);

	/*
	 * Clear the compiled BSS segment in OpenBSD code
	 */
	bzero(edata, end - edata);

	/*
	 *  Reserve symbol table space. If invalid pointers no table.
	 */
	ssym = (char *)*(u_int64_t *)end;
	esym = (char *)*((u_int64_t *)end + 1);
	ekern = esym;
	if (((long)ssym - (long)end) < 0 ||
	    ((long)ssym - (long)end) > 0x1000 ||
	    ssym[0] != ELFMAG0 || ssym[1] != ELFMAG1 ||
	    ssym[2] != ELFMAG2 || ssym[3] != ELFMAG3 ) {
		ssym = NULL;
		esym = NULL;
		ekern = end;
	}

	/*
	 *  Initialize the system type and set up memory layout
	 *  Note that some systems have more complex memory setup.
	 */
	bios_ident();

bios_printf("SR=%08x\n", getsr()); /* leave this in for now. need to see sr */

	/*
	 * Determine system type and set up configuration record data.
	 */
	switch (sys_config.system_type) {
#if defined(TGT_O2)
	case SGI_O2:
		bios_printf("Found SGI-IP32, setting up.\n");
		strlcpy(cpu_model, "SGI-O2 (IP32)", sizeof(cpu_model));
		sys_config.cons_ioaddr[0] = MACE_ISA_SER1_OFFS;
		sys_config.cons_ioaddr[1] = MACE_ISA_SER2_OFFS;
		sys_config.cons_baudclk = 1843200;		/*XXX*/
		sys_config.cons_iot = &macebus_tag;
		sys_config.local.bus_base = 0x0;		/*XXX*/
		sys_config.pci_io[0].bus_base = 0xffffffff00000000;/*XXX*/
		sys_config.pci_mem[0].bus_base = 0xffffffff00000000;/*XXX*/
		sys_config.pci_mem[0].bus_base_dma = 0x00000000;/*XXX*/
		sys_config.pci_mem[0].bus_reverse = my_endian;
		sys_config.cpu[0].tlbwired = 2;

		crime_configure_memory();

		sys_config.cpu[0].clock = 180000000;  /* Reasonable default */
		cp = Bios_GetEnvironmentVariable("cpufreq");
		if (cp && atoi(cp, 10, NULL) > 100)
			sys_config.cpu[0].clock = atoi(cp, 10, NULL) * 1000000;

		/* R1xK O2's are one disk slot machines. Offset slotno */
		switch ((cp0_get_prid() >> 8) & 0xff) {
		case MIPS_R10000:
		case MIPS_R12000:
			bootdriveoffs = -1;
			break;
		}
		/* R12K O2's must run with DSD on */
		switch ((cp0_get_prid() >> 8) & 0xff) {
		case MIPS_R12000:
			setsr(getsr() | SR_DSD);
			break;
		}
		break;
#endif

#if defined(TGT_ORIGIN200) || defined(TGT_ORIGIN2000)
	case SGI_O200:
		bios_printf("Found SGI-IP27, setting up.\n");
		strlcpy(cpu_model, "SGI- Origin200 (IP27)", sizeof(cpu_model));

		kl_scan_config(0);

		sys_config.cons_ioaddr[0] = kl_get_console_base();
		sys_config.cons_ioaddr[1] = kl_get_console_base() - 8;
		sys_config.cons_baudclk = 22000000 / 3;	/*XXX*/
		sys_config.cons_iot = &xbowmux_tag;
		sys_config.local.bus_base = 0x0;		/*XXX*/
		sys_config.pci_io[0].bus_base = 0xffffffff00000000;/*XXX*/
		sys_config.pci_mem[0].bus_base = 0xffffffff00000000;/*XXX*/
		sys_config.pci_mem[0].bus_base_dma = 0x00000000;/*XXX*/
		sys_config.pci_mem[0].bus_reverse = my_endian;
		sys_config.cpu[0].tlbwired = 2;
		break;
#endif

	default:
		bios_printf("Kernel doesn't support this system type!\n");
		bios_printf("Halting system.\n");
		Bios_Halt();
		while(1);
	}

	/*
	 * Look at arguments passed to us and compute boothowto.
	 * Default to SINGLE and ASKNAME if no args or
	 * SINGLE and DFLTROOT if this is a ramdisk kernel.
	 */
#ifdef RAMDISK_HOOKS
	boothowto = RB_SINGLE | RB_DFLTROOT;
#else
	boothowto = RB_SINGLE | RB_ASKNAME;
#endif /* RAMDISK_HOOKS */

	dobootopts(argc, argv);

	/*
	 *  Figure out where we was booted from.
	 */
	cp = Bios_GetEnvironmentVariable("OSLoadPartition");
	if (cp == NULL)
		cp = "unknown";
	if (makebootdev(cp, bootdriveoffs))
		bios_printf("Boot device unrecognized: '%s'\n", cp);

	/*
	 * Read platform-specific environment variables.
	 */
	switch (sys_config.system_type) {
#if defined(TGT_O2)
	case SGI_O2:
		/* get ethernet address from ARCBIOS */
		cp = Bios_GetEnvironmentVariable("eaddr");
		if (cp != NULL && strlen(cp) > 0)
			strlcpy(bios_enaddr, cp, sizeof bios_enaddr);
		break;
#endif
	default:
		break;
	}

	/*
	 *  Set pagesize to enable use of page macros and functions.
	 *  Commit available memory to UVM system
	 */
	uvmexp.pagesize = PAGE_SIZE;
	uvm_setpagesize();

	for (i = 0; i < MAXMEMSEGS && mem_layout[i].mem_first_page != 0; i++) {
		u_int32_t fp, lp;
		u_int32_t firstkernpage, lastkernpage;
		paddr_t firstkernpa, lastkernpa;

		if (IS_XKPHYS((vaddr_t)start))
			firstkernpa = XKPHYS_TO_PHYS((vaddr_t)start);
		else
			firstkernpa = KSEG0_TO_PHYS((vaddr_t)start);
		if (IS_XKPHYS((vaddr_t)ekern))
			lastkernpa = XKPHYS_TO_PHYS((vaddr_t)ekern);
		else
			lastkernpa = KSEG0_TO_PHYS((vaddr_t)ekern);

		firstkernpage = atop(trunc_page(firstkernpa));
		lastkernpage = atop(round_page(lastkernpa));

		fp = mem_layout[i].mem_first_page;
		lp = mem_layout[i].mem_last_page;

		/* Account for kernel and kernel symbol table */
		if (fp >= firstkernpage && lp < lastkernpage)
			continue;	/* In kernel */

		if (lp < firstkernpage || fp > lastkernpage) {
			uvm_page_physload(fp, lp, fp, lp, VM_FREELIST_DEFAULT);
			continue;	/* Outside kernel */
		}

		if (fp >= firstkernpage)
			fp = lastkernpage;
		else if (lp < lastkernpage)
			lp = firstkernpage;
		else { /* Need to split! */
			u_int32_t xp = firstkernpage;
			uvm_page_physload(fp, xp, fp, xp, VM_FREELIST_DEFAULT);
			fp = lastkernpage;
		}
		if (lp >= fp)
			uvm_page_physload(fp, lp, fp, lp, VM_FREELIST_DEFAULT);
	}


	switch (sys_config.system_type) {
#if defined(TGT_O2)
	case SGI_O2:
		sys_config.cpu[0].type = (cp0_get_prid() >> 8) & 0xff;
		sys_config.cpu[0].vers_maj = (cp0_get_prid() >> 4) & 0x0f;
		sys_config.cpu[0].vers_min = cp0_get_prid() & 0x0f;
		sys_config.cpu[0].fptype = (cp1_get_prid() >> 8) & 0xff;
		sys_config.cpu[0].fpvers_maj = (cp1_get_prid() >> 4) & 0x0f;
		sys_config.cpu[0].fpvers_min = cp1_get_prid() & 0x0f;

		/*
		 *  Configure TLB.
		 */
		switch(sys_config.cpu[0].type) {
		case MIPS_RM7000:
			/* Rev A (version >= 2) CPU's have 64 TLB entries. */
			if (sys_config.cpu[0].vers_maj < 2) {
				sys_config.cpu[0].tlbsize = 48;
			} else {
				sys_config.cpu[0].tlbsize = 64;
			}
			break;

		case MIPS_R10000:
		case MIPS_R12000:
			sys_config.cpu[0].tlbsize = 64;
			break;

		default:
			sys_config.cpu[0].tlbsize = 48;
			break;
		}
		break;
#endif
	default:
		break;
	}

	/*
	 *  Configure Cache.
	 */
	switch(sys_config.cpu[0].type) {
	case MIPS_R10000:
	case MIPS_R12000:
	case MIPS_R14000:
		sys_config.cpu[0].cfg_reg = Mips10k_ConfigCache();
		sys_config._SyncCache = Mips10k_SyncCache;
		sys_config._InvalidateICache = Mips10k_InvalidateICache;
		sys_config._InvalidateICachePage = Mips10k_InvalidateICachePage;
		sys_config._SyncDCachePage = Mips10k_SyncDCachePage;
		sys_config._HitSyncDCache = Mips10k_HitSyncDCache;
		sys_config._IOSyncDCache = Mips10k_IOSyncDCache;
		sys_config._HitInvalidateDCache = Mips10k_HitInvalidateDCache;
		break;

	default:
		sys_config.cpu[0].cfg_reg = Mips5k_ConfigCache();
		sys_config._SyncCache = Mips5k_SyncCache;
		sys_config._InvalidateICache = Mips5k_InvalidateICache;
		sys_config._InvalidateICachePage = Mips5k_InvalidateICachePage;
		sys_config._SyncDCachePage = Mips5k_SyncDCachePage;
		sys_config._HitSyncDCache = Mips5k_HitSyncDCache;
		sys_config._IOSyncDCache = Mips5k_IOSyncDCache;
		sys_config._HitInvalidateDCache = Mips5k_HitInvalidateDCache;
		break;
	}

	/*
	 *  Last chance to call the bios. Wiping the TLB means
	 *  bios data areas are demapped on most systems.
	 *  O2's are OK. Does not have mapped bios text or data.
	 */
	delay(20*1000);		/* Let any uart fifo drain... */
	tlb_set_wired(0);
	tlb_flush(sys_config.cpu[0].tlbsize);
	tlb_set_wired(sys_config.cpu[0].tlbwired);

#if 0
	/* XXX Save the following as an example on how to optimize I/O mapping */

	/*
	 *  Set up some fixed mappings. These are so frequently
	 *  used so faulting them in will waste to many cycles.
	 */
	if (sys_config.system_type == MOMENTUM_CP7000G ||
	    sys_config.system_type == MOMENTUM_CP7000 ||
	    sys_config.system_type == GALILEO_EV64240) {
		struct tlb tlb;

		tlb.tlb_mask = PG_SIZE_16M;
#if defined(LP64)
		tlb.tlb_hi = vad_to_vpn(0xfffffffffc000000) | 1;
		tlb.tlb_lo0 = vad_to_pfn(0xfffffffff4000000) | PG_IOPAGE;
#else
		tlb.tlb_hi = vad_to_vpn(0xfc000000) | 1;
		tlb.tlb_lo0 = vad_to_pfn(0xf4000000) | PG_IOPAGE;
#endif
		tlb.tlb_lo1 = vad_to_pfn(sys_config.cons_ioaddr[0]) | PG_IOPAGE;
		tlb_write_indexed(2, &tlb);

		if (sys_config.system_type == GALILEO_EV64240) {
			tlb.tlb_mask = PG_SIZE_16M;
			tlb.tlb_hi = vad_to_vpn(0xf8000000) | 1;
			tlb.tlb_lo0 = vad_to_pfn(sys_config.pci_io[0].bus_base) | PG_IOPAGE;
			tlb.tlb_lo1 = vad_to_pfn(sys_config.pci_mem[0].bus_base) | PG_IOPAGE;
			tlb_write_indexed(3, &tlb);
		}
	}
/* XXX */
#endif

#if defined(TGT_ORIGIN200) || defined(TGT_ORIGIN2000)
	/*
	 *  If an IP27 system set up Node 0's HUB.
	 */
	if (sys_config.system_type == SGI_O200) {
		IP27_LHUB_S(PI_REGION_PRESENT, 1);
		IP27_LHUB_S(PI_CALIAS_SIZE, PI_CALIAS_SIZE_0);
	}
#endif

	/*
	 *  Get a console, very early but after initial mapping setup.
	 */
	consinit();
	printf("Initial setup done, switching console.\n");

	/*
	 * Init message buffer.
	 */
	msgbufbase = (caddr_t)pmap_steal_memory(MSGBUFSIZE, NULL,NULL);
	initmsgbuf(msgbufbase, MSGBUFSIZE);

	/*
	 * Allocate U page(s) for proc[0], pm_tlbpid 1.
	 */
	proc0.p_addr = proc0paddr = curprocpaddr =
	    (struct user *)pmap_steal_memory(USPACE, NULL, NULL);
	proc0.p_md.md_regs = (struct trap_frame *)&proc0paddr->u_pcb.pcb_regs;
	tlb_set_pid(1);

	/*
	 * Allocate system data structures.
	 */
	i = (vsize_t)allocsys(NULL);
	sd = (caddr_t)pmap_steal_memory(i, NULL, NULL);
	allocsys(sd);

	/*
	 * Bootstrap VM system.
	 */
	pmap_bootstrap();


	/*
	 * Copy down exception vector code.
	 */
	bcopy(tlb_miss_tramp, (char *)TLB_MISS_EXC_VEC,
	    e_tlb_miss_tramp - tlb_miss_tramp);
	bcopy(xtlb_miss_tramp, (char *)XTLB_MISS_EXC_VEC,
	    e_xtlb_miss_tramp - xtlb_miss_tramp);
	bcopy(exception, (char *)CACHE_ERR_EXC_VEC, e_exception - exception);
	bcopy(exception, (char *)GEN_EXC_VEC, e_exception - exception);

	/*
	 *  Turn off bootstrap exception vectors.
	 */
	setsr(getsr() & ~SR_BOOT_EXC_VEC);
	proc0.p_md.md_regs->sr = getsr();

	/*
	 * Clear out the I and D caches.
	 */
	Mips_SyncCache();

#ifdef DDB
	db_machine_init();
	if (boothowto & RB_KDB)
		Debugger();
#endif

	/*
	 *  Return new stack pointer.
	 */
	return ((caddr_t)proc0paddr + USPACE - 64);
}

/*
 * Allocate space for system data structures. Doesn't need to be mapped.
 */
caddr_t
allocsys(caddr_t v)
{
	caddr_t start;

	start = v;

#define	valloc(name, type, num) \
	    (name) = (type *)v; v = (caddr_t)((name)+(num))
#ifdef SYSVMSG
	valloc(msgpool, char, msginfo.msgmax);
	valloc(msgmaps, struct msgmap, msginfo.msgseg);
	valloc(msghdrs, struct msg, msginfo.msgtql);
	valloc(msqids, struct msqid_ds, msginfo.msgmni);
#endif

	return(v);
}


/*
 *  Decode boot options.
 */
static void
dobootopts(int argc, void *argv)
{
	char *cp;
	int i;

	/* XXX Should this be done differently, eg env vs. args? */
	for (i = 1; i < argc; i++) {
		if (bios_is_32bit)
			cp = (char *)(long)((int32_t *)argv)[i];
		else
			cp = ((char **)argv)[i];
		if (cp != NULL && strncmp(cp, "OSLoadOptions=", 14) == 0) {
			if (strcmp(&cp[14], "auto") == 0)
					boothowto &= ~(RB_SINGLE|RB_ASKNAME);
			else if (strcmp(&cp[14], "single") == 0)
					boothowto |= RB_SINGLE;
			else if (strcmp(&cp[14], "debug") == 0)
					boothowto |= RB_KDB;
		}
	}

	/* Catch serial consoles on O2's */
	cp = Bios_GetEnvironmentVariable("ConsoleOut");
	if (cp != NULL && strncmp(cp, "serial", 6) == 0)
		boothowto |= RB_SERCONS;
}


/*
 * Console initialization: called early on from main,
 * before vm init or startup.  Do enough configuration
 * to choose and initialize a console.
 */
void
consinit()
{
	if (console_ok) {
		return;
	}
	cninit();
	console_ok = 1;
}

/*
 * cpu_startup: allocate memory for variable-sized tables,
 * initialize cpu, and do autoconfiguration.
 */
void
cpu_startup()
{
	vaddr_t minaddr, maxaddr;
#ifdef PMAPDEBUG
	extern int pmapdebug;
	int opmapdebug = pmapdebug;

	pmapdebug = 0;	/* Shut up pmap debug during bootstrap */
#endif

	/*
	 * Good {morning,afternoon,evening,night}.
	 */
	printf(version);
	printf("real mem = %u (%uMB)\n", ptoa(physmem),
	    ptoa(physmem)/1024/1024);
	printf("rsvd mem = %u (%uMB)\n", ptoa(rsvdmem),
	    ptoa(rsvdmem)/1024/1024);

	/*
	 * Determine how many buffers to allocate.
	 * We allocate bufcachepercent% of memory for buffer space.
	 */
	if (bufpages == 0)
		bufpages = physmem * bufcachepercent / 100;

	/* Restrict to at most 25% filled kvm */
	if (bufpages >
	    (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) / PAGE_SIZE / 4) 
		bufpages = (VM_MAX_KERNEL_ADDRESS-VM_MIN_KERNEL_ADDRESS) /
		    PAGE_SIZE / 4;

	/*
	 * Allocate a submap for exec arguments.  This map effectively
	 * limits the number of processes exec'ing at any time.
	 */
	minaddr = vm_map_min(kernel_map);
	exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
	    16 * NCARGS, VM_MAP_PAGEABLE, FALSE, NULL);
	/* Allocate a submap for physio */
	phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
	    VM_PHYS_SIZE, 0, FALSE, NULL);

#ifdef PMAPDEBUG
	pmapdebug = opmapdebug;
#endif
	printf("avail mem = %u (%uMB)\n", ptoa(uvmexp.free),
	    ptoa(uvmexp.free)/1024/1024);

	extent_malloc_flags = EX_MALLOCOK;

	/*
	 * Set up CPU-specific registers, cache, etc.
	 */
	initcpu();

	/*
	 * Set up buffers, so they can be used to read disk labels.
	 */
	bufinit();

	/*
	 * Configure the system.
	 */
	if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
		user_config();
#else
		printf("kernel does not support -c; continuing..\n");
#endif
	}
}

/*
 * machine dependent system variables.
 */
int
cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
	int *name;
	u_int namelen;
	void *oldp;
	size_t *oldlenp;
	void *newp;
	size_t newlen;
	struct proc *p;
{
	/* all sysctl names at this level are terminal */
	if (namelen != 1)
		return ENOTDIR;		/* overloaded */

	switch (name[0]) {
	default:
		return EOPNOTSUPP;
	}
}

/*
 * Set registers on exec for native exec format. For o64/64.
 */
void
setregs(p, pack, stack, retval)
	struct proc *p;
	struct exec_package *pack;
	u_long stack;
	register_t *retval;
{
	extern struct proc *machFPCurProcPtr;
#if 0
/* XXX should check validity of header and perhaps be 32/64 indep. */
	Elf64_Ehdr *eh = pack->ep_hdr;

	if ((((eh->e_flags & EF_MIPS_ABI) != E_MIPS_ABI_NONE) &&
	    ((eh->e_flags & EF_MIPS_ABI) != E_MIPS_ABI_O32)) ||
	    ((eh->e_flags & EF_MIPS_ARCH) >= E_MIPS_ARCH_3) ||
	    (eh->e_ident[EI_CLASS] != ELFCLASS32)) {
		p->p_md.md_flags |= MDP_O32;
	}
#endif

#if !defined(__LP64__)
	p->p_md.md_flags |= MDP_O32;
#else
	p->p_md.md_flags &= ~MDP_O32;
#endif

	bzero((caddr_t)p->p_md.md_regs, sizeof(struct trap_frame));
	p->p_md.md_regs->sp = stack;
	p->p_md.md_regs->pc = pack->ep_entry & ~3;
	p->p_md.md_regs->t9 = pack->ep_entry & ~3; /* abicall req */
#if defined(__LP64__)
	p->p_md.md_regs->sr = SR_FR_32 | SR_XX | SR_KSU_USER | SR_KX | SR_UX |
	    SR_EXL | SR_INT_ENAB;
	if (sys_config.cpu[0].type == MIPS_R12000 &&
	    sys_config.system_type == SGI_O2)
		p->p_md.md_regs->sr |= SR_DSD;
#else
	p->p_md.md_regs->sr = SR_KSU_USER|SR_XX|SR_EXL|SR_INT_ENAB;
#endif
	p->p_md.md_regs->sr |= idle_mask & SR_INT_MASK;
	p->p_md.md_regs->ic = (idle_mask << 8) & IC_INT_MASK;
	p->p_md.md_flags &= ~MDP_FPUSED;
	if (machFPCurProcPtr == p)
		machFPCurProcPtr = (struct proc *)0;
	p->p_md.md_ss_addr = 0;
	p->p_md.md_pc_ctrl = 0;
	p->p_md.md_watch_1 = 0;
	p->p_md.md_watch_2 = 0;

	retval[1] = 0;
}


int	waittime = -1;

void
boot(int howto)
{

	/* take a snap shot before clobbering any registers */
	if (curproc)
		savectx(curproc->p_addr, 0);

#ifdef DEBUG
	if (panicstr)
		stacktrace();
#endif

	if (cold) {
		/*
		 * If the system is cold, just halt, unless the user
		 * explicitely asked for reboot.
		 */
		if ((howto & RB_USERREQ) == 0)
			howto |= RB_HALT;
		goto haltsys;
	}

	boothowto = howto;
	if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
		extern struct proc proc0;
		/* fill curproc with live object */
		if (curproc == NULL)
			curproc = &proc0;
		/*
		 * Synchronize the disks....
		 */
		waittime = 0;
		vfs_shutdown();

		/*
		 * If we've been adjusting the clock, the todr
		 * will be out of synch; adjust it now.
		 */
		if ((howto & RB_TIMEBAD) == 0) {
			resettodr();
		} else {
			printf("WARNING: not updating battery clock\n");
		}
	}

	(void) splhigh();		/* extreme priority */

	if (howto & RB_DUMP)
		dumpsys();

haltsys:
	doshutdownhooks();

	if (howto & RB_HALT) {
		if (howto & RB_POWERDOWN) {
			printf("System Power Down.\n");
			delay(1000000);
			Bios_PowerDown();
		} else {
			printf("System Halt.\n");
			delay(1000000);
			Bios_EnterInteractiveMode();
		}
		printf("Didn't want to die!!! Reset manually.\n");
	} else {
		printf("System restart.\n");
		delay(1000000);
		Bios_Reboot();
		printf("Restart failed!!! Reset manually.\n");
	}
	for (;;) ;
	/*NOTREACHED*/
}

int	dumpmag = (int)0x8fca0101;	/* magic number for savecore */
int	dumpsize = 0;		/* also for savecore */
long	dumplo = 0;

void
dumpconf(void)
{
	int nblks;

	if (dumpdev == NODEV ||
	    (nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
		return;
	if (nblks <= ctod(1))
		return;

	dumpsize = ptoa(physmem);
	if (dumpsize > btoc(dbtob(nblks - dumplo)))
		dumpsize = btoc(dbtob(nblks - dumplo));
	else if (dumplo == 0)
		dumplo = nblks - btodb(ctob(physmem));

	/*
	 * Don't dump on the first page
	 * in case the dump device includes a disk label.
	 */
	if (dumplo < btodb(PAGE_SIZE))
		dumplo = btodb(PAGE_SIZE);
}

/*
 * Doadump comes here after turning off memory management and
 * getting on the dump stack, either when called above, or by
 * the auto-restart code.
 */
void
dumpsys()
{
	extern int msgbufmapped;

	msgbufmapped = 0;
	if (dumpdev == NODEV)
		return;
	/*
	 * For dumps during autoconfiguration,
	 * if dump device has already configured...
	 */
	if (dumpsize == 0)
		dumpconf();
	if (dumplo < 0)
		return;
	printf("\ndumping to dev %x, offset %d\n", dumpdev, dumplo);
	printf("dump not yet implemented");
#if 0 /* XXX HAVE TO FIX XXX */
	switch (error = (*bdevsw[major(dumpdev)].d_dump)(dumpdev, dumplo,)) {

	case ENXIO:
		printf("device bad\n");
		break;

	case EFAULT:
		printf("device not ready\n");
		break;

	case EINVAL:
		printf("area improper\n");
		break;

	case EIO:
		printf("i/o error\n");
		break;

	default:
		printf("error %d\n", error);
		break;

	case 0:
		printf("succeeded\n");
	}
#endif
}

void
initcpu()
{
}

/*
 * Convert "xx:xx:xx:xx:xx:xx" string to ethernet hardware address.
 */
void
enaddr_aton(const char *s, u_int8_t *a)
{
	int i;

	if (s != NULL) {
		for(i = 0; i < 6; i++) {
			a[i] = atoi(s, 16, &s);
			if (*s == ':')
				s++;
		}
	}
}

/*
 * Convert an ASCII string into an integer.
 */
static int
atoi(const char *s, int b, const char **o)
{
	int c;
	unsigned base = b, d;
	int neg = 0, val = 0;

	if (s == NULL || *s == 0) {
		if (o != NULL)
			*o = s;
		return 0;
	}

	/* skip spaces if any */
	do {
		c = *s++;
	} while (c == ' ' || c == '\t');

	/* parse sign, allow more than one (compat) */
	while (c == '-') {
		neg = !neg;
		c = *s++;
	}

	/* parse base specification, if any */
	if (c == '0') {
		c = *s++;
		switch (c) {
		case 'X':
		case 'x':
			base = 16;
			c = *s++;
			break;
		case 'B':
		case 'b':
			base = 2;
			c = *s++;
			break;
		default:
			base = 8;
		}
	}

	/* parse number proper */
	for (;;) {
		if (c >= '0' && c <= '9')
			d = c - '0';
		else if (c >= 'a' && c <= 'z')
			d = c - 'a' + 10;
		else if (c >= 'A' && c <= 'Z')
			d = c - 'A' + 10;
		else
			break;
		val *= base;
		val += d;
		c = *s++;
	}
	if (neg)
		val = -val;
	if (o != NULL)
		*o = s - 1;
	return val;
}

/*
 *  RM7000 Performance counter support.
 */

int
rm7k_perfcntr(cmd, arg1, arg2, arg3)
	int cmd;
	long  arg1, arg2, arg3;
{
	int result;
	quad_t cntval;
	struct proc *p = curproc;


	switch(cmd) {
	case PCNT_FNC_SELECT:
		if ((arg1 & 0xff) > PCNT_SRC_MAX ||
		   (arg1 & ~(PCNT_CE|PCNT_UM|PCNT_KM|0xff)) != 0) {
			result = EINVAL;
			break;
		}
#ifdef DEBUG
printf("perfcnt select %x, proc %p\n", arg1, p);
#endif
		p->p_md.md_pc_count = 0;
		p->p_md.md_pc_spill = 0;
		p->p_md.md_pc_ctrl = arg1;
		result = 0;
		break;

	case PCNT_FNC_READ:
		cntval = p->p_md.md_pc_count;
		cntval += (quad_t)p->p_md.md_pc_spill << 31;
		result = copyout(&cntval, (void *)arg1, sizeof(cntval));
		break;

	default:
#ifdef DEBUG
printf("perfcnt error %d\n", cmd);
#endif
		result = -1;
		break;
	}
	return(result);
}

/*
 *  Called when the performance counter d31 gets set.
 *  Increase spill value and reset d31.
 */
void
rm7k_perfintr(trapframe)
	struct trap_frame *trapframe;
{
	struct proc *p = curproc;

	printf("perfintr proc %p!\n", p);
	cp0_setperfcount(cp0_getperfcount() & 0x7fffffff);
	if (p != NULL) {
		p->p_md.md_pc_spill++;
	}
}

int
rm7k_watchintr(trapframe)
	struct trap_frame *trapframe;
{
	return(0);
}

#ifdef DEBUG
/*
 *	Dump TLB contents.
 */
void
dump_tlb()
{
char *attr[] = {
	"CWTNA", "CWTA ", "UCBL ", "CWB  ", "RES  ", "RES  ", "UCNB ", "BPASS"
};

	int tlbno, last;
	struct tlb_entry tlb;

	last = 64;

	for (tlbno = 0; tlbno < last; tlbno++) {
		tlb_read(tlbno, &tlb);

		if (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V) {
			bios_printf("%2d v=%p", tlbno, tlb.tlb_hi & 0xffffffffffffff00);
			bios_printf("/%02x ", tlb.tlb_hi & 0xff);

			if (tlb.tlb_lo0 & PG_V) {
				bios_printf("0x%09x ", pfn_to_pad(tlb.tlb_lo0));
				bios_printf("%c", tlb.tlb_lo0 & PG_M ? 'M' : ' ');
				bios_printf("%c", tlb.tlb_lo0 & PG_G ? 'G' : ' ');
				bios_printf(" %s ", attr[(tlb.tlb_lo0 >> 3) & 7]);
			} else {
				bios_printf("invalid             ");
			}

			if (tlb.tlb_lo1 & PG_V) {
				bios_printf("0x%08x ", pfn_to_pad(tlb.tlb_lo1));
				bios_printf("%c", tlb.tlb_lo1 & PG_M ? 'M' : ' ');
				bios_printf("%c", tlb.tlb_lo1 & PG_G ? 'G' : ' ');
				bios_printf(" %s ", attr[(tlb.tlb_lo1 >> 3) & 7]);
			} else {
				bios_printf("invalid             ");
			}
			bios_printf(" sz=%x", tlb.tlb_mask);
		}
		else {
			bios_printf("%2d v=invalid    ", tlbno);
		}
		bios_printf("\n");
	}
}
#endif