code:
/*	$OpenBSD: l1.c,v 1.9 2014/11/25 19:08:42 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Communication with the L1 controller, on IP35 systems.
 * We use a direct 57600 bps serial link from each processor to the L1 chip.
 * Information is sent as ppp-encoded packets.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>

#include <machine/autoconf.h>
#include <machine/mnode.h>
#include <sgi/xbow/hub.h>

#include <dev/ic/ns16550reg.h>
#include <dev/ic/comreg.h>

#include <net/ppp_defs.h>

#include <sgi/sgi/ip27.h>
#include <sgi/sgi/l1.h>

/*
 * L1 communication defines
 */

/* packet types */
#define	L1PKT_REQUEST	0x00
#define	L1PKT_RESPONSE	0x20
#define	L1PKT_EVENT	0x40

/* packet subchannels */
#define	L1CH_CPU0	0x00	/* exclusive channel for cpu 0 */
#define	L1CH_CPU1	0x01	/* exclusive channel for cpu 1 */
#define	L1CH_CPU2	0x02	/* exclusive channel for cpu 2 */
#define	L1CH_CPU3	0x03	/* exclusive channel for cpu 3 */
#define	L1CH_CONSOLE	0x04	/* L1 console */
/* 05..0f reserved */
/* 10..1f available for operating system */
#define	L1CH_MISC	0x10

/* argument encoding */
#define	L1_ARG_INT		0x00	/* followed by 32 bit BE value */
#define	L1_ARG_ASCII		0x01	/* followed by NUL terminated string */
#define	L1_ARG_BINARY		0x80	/* length in low 7 bits */

int	l1_serial_getc(int16_t);
int	l1_serial_putc(int16_t, u_char);
int	l1_serial_ppp_write(int16_t, uint16_t *, u_char, int);
int	l1_serial_ppp_read(int16_t, int);
int	l1_packet_put(int16_t, u_char *, size_t);
int	l1_packet_get(int16_t, u_char *, size_t);
static inline
void	l1_packet_put_be32(u_char *, uint32_t);
static inline
void	l1_packet_put_be16(u_char *, uint16_t);
static inline
uint32_t l1_packet_get_be32(u_char *);
int	l1_packet_get_int(u_char **, size_t *, uint32_t *);
int	l1_packet_get_ascii(u_char **, size_t *, char **);
int	l1_packet_get_binary(u_char **, size_t *, u_char **, size_t *);
size_t	l1_command_build(u_char *, size_t, uint32_t, uint16_t, int, ...);
int	l1_receive_response(int16_t, u_char *, size_t *);
int	l1_response_to_errno(uint32_t);
int	l1_read_board_ia(int16_t, int, u_char **, size_t *);

static inline
size_t	ia_skip(u_char *, size_t, size_t);

/* l1_packet_get() return values */
#define	L1PG_TIMEOUT		-1
#define	L1PG_BADCRC		-2
#define	L1PG_SHORTPACKET	-3

/*
 * Basic serial routines (polled)
 */

#define	L1_UART_ADDRESS(nasid, r) \
	IP27_RHSPEC_ADDR(nasid, HSPEC_L1_UART(r))

int
l1_serial_getc(int16_t nasid)
{
	uint64_t lsr;
	int n;

	for (n = 1000000; n != 0; n--) {
		lsr = *(volatile uint64_t *)L1_UART_ADDRESS(nasid, com_lsr);
		if ((lsr & LSR_RXRDY) != 0)
			break;
	}

	if (n == 0) {
#ifdef L1_DEBUG
		printf("%s: RX timeout, lsr %02llx\n", __func__, lsr);
#endif
		return -1;
	}

	return *(volatile uint64_t *)L1_UART_ADDRESS(nasid, com_data) & 0xff;

}

int
l1_serial_putc(int16_t nasid, u_char val)
{
	uint64_t lsr;
	int n;

	for (n = 1000000; n != 0; n--) {
		lsr = *(volatile uint64_t *)L1_UART_ADDRESS(nasid, com_lsr);
		if ((lsr & LSR_TXRDY) != 0)
			break;
	}

	if (n == 0) {
#ifdef L1_DEBUG
		printf("%s: TX timeout, lsr %02llx\n", __func__, lsr);
#endif
		return EWOULDBLOCK;
	}

	*(volatile uint64_t *)L1_UART_ADDRESS(nasid, com_data) = (uint64_t)val;
	return 0;
}

/*
 * Single character routines, with optional ppp frame escaping, and optional
 * ppp crc update.
 */

/*
 * FCS lookup table as calculated by genfcstab.
 * Straight from <net/ppp_tty.c>, only 512 bytes long; probably not worth
 * trying to share with outsmart config(8) machinery...
 */
static const u_int16_t fcstab[256] = {
	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
};

int
l1_serial_ppp_write(int16_t nasid, uint16_t *crc, u_char data, int escape)
{
	/* update crc if necessary */
	if (crc != NULL)
		*crc = PPP_FCS(*crc, data);

	/* escape data if necessary */
	if (escape && (data == PPP_FLAG || data == PPP_ESCAPE)) {
		if (l1_serial_putc(nasid, PPP_ESCAPE) != 0)
			return EWOULDBLOCK;
		data ^= PPP_TRANS;
	}

	return l1_serial_putc(nasid, data);
}

int
l1_serial_ppp_read(int16_t nasid, int unescape)
{
	int data;

	if ((data = l1_serial_getc(nasid)) < 0)
		return data;

	/* unescape data if necessary */
	if (unescape && data == PPP_ESCAPE) {
		if ((data = l1_serial_getc(nasid)) < 0)
			return data;
		data ^= PPP_TRANS;
	}

	return data;
}

/*
 * Complete ppp packet emission and reception.
 */

int
l1_packet_put(int16_t nasid, u_char *packet, size_t len)
{
	uint16_t crc = PPP_INITFCS;

	/* send incoming packet flag */
	if (l1_serial_ppp_write(nasid, NULL, PPP_FLAG, 0) != 0)
		return EWOULDBLOCK;

	/* send packet data */
	while (len-- != 0)
		if (l1_serial_ppp_write(nasid, &crc, *packet++, 1) != 0)
			return EWOULDBLOCK;

	/* send crc */
	crc ^= PPP_INITFCS;
	if (l1_serial_ppp_write(nasid, NULL, crc & 0xff, 1) != 0)
		return EWOULDBLOCK;
	if (l1_serial_ppp_write(nasid, NULL, (crc >> 8) & 0xff, 1) != 0)
		return EWOULDBLOCK;

	/* send final packet byte flag */
	if (l1_serial_ppp_write(nasid, NULL, PPP_FLAG, 0) != 0)
		return EWOULDBLOCK;

	return 0;
}

int
l1_packet_get(int16_t nasid, u_char *buf, size_t buflen)
{
	uint16_t crc;
	size_t rcvlen;
	int data;

	/* wait for incoming packet flag */
	for (;;) {
		data = l1_serial_ppp_read(nasid, 0);
		if (data < 0)
			return L1PG_TIMEOUT;
		if (data == PPP_FLAG)
			break;
	}

	/* read packet */
	rcvlen = 0;
	crc = PPP_INITFCS;
	for (;;) {
		data = l1_serial_ppp_read(nasid, 1);
		if (data < 0)
			return L1PG_TIMEOUT;
		if (data == PPP_FLAG)	/* end of packet */
			break;
		if (rcvlen < buflen)
			buf[rcvlen] = data;
		rcvlen++;
		crc = PPP_FCS(crc, data);
	}

	if (rcvlen < 2) {
#ifdef L1_DEBUG
		printf("%s: short packet\n", __func__);
#endif
		return L1PG_SHORTPACKET;
	}

	/* check CRC */
	rcvlen -= 2;	/* crc bytes */
	if (crc != PPP_GOODFCS) {
#ifdef L1_DEBUG
		printf("%s: CRC error (%04x)\n", __func__, crc);
#endif
		return L1PG_BADCRC;
	}

	return rcvlen;
}

/*
 * L1 packet construction and deconstruction helpers
 */

static inline void
l1_packet_put_be32(u_char *buf, uint32_t data)
{
	*buf++ = data >> 24;
	*buf++ = data >> 16;
	*buf++ = data >> 8;
	*buf++ = data;
}

static inline void
l1_packet_put_be16(u_char *buf, uint16_t data)
{
	*buf++ = data >> 8;
	*buf++ = data;
}

static inline uint32_t
l1_packet_get_be32(u_char *buf)
{
	uint32_t data;

	data = *buf++;
	data <<= 8;
	data |= *buf++;
	data <<= 8;
	data |= *buf++;
	data <<= 8;
	data |= *buf++;

	return data;
}

int
l1_packet_get_int(u_char **buf, size_t *buflen, uint32_t *rval)
{
	u_char *b = *buf;

	if (*buflen < 5)
		return EIO;

	if (*b++ != L1_ARG_INT)
		return EINVAL;

	*rval = l1_packet_get_be32(b);

	b += 4;
	*buf = b;
	*buflen -= 5;

	return 0;
}

int
l1_packet_get_ascii(u_char **buf, size_t *buflen, char **rval)
{
	u_char *b = *buf;
	u_char *s, *e;

	if (*buflen < 2)
		return EIO;

	if (*b != L1_ARG_ASCII)
		return EINVAL;

	/* check for a terminating NUL within the given bounds */
	e = b + *buflen;
	for (s = b + 1; s != e; s++)
		if (*s == '\0')
			break;
	if (s == e)
		return ENOMEM;

	*rval = (char *)b + 1;

	s++;
	*buflen -= s - b;
	*buf = s;

	return 0;
}

int
l1_packet_get_binary(u_char **buf, size_t *buflen, u_char **rval, size_t *rlen)
{
	u_char *b = *buf;
	size_t datalen;

	if (*buflen < 1)
		return EIO;

	if ((*b & L1_ARG_BINARY) == 0)
		return EINVAL;

	datalen = *b & ~L1_ARG_BINARY;
	if (*buflen < 1 + datalen)
		return ENOMEM;

	b++;
	*rval = b;
	*rlen = datalen;
	*buflen -= 1 + datalen;
	b += datalen;
	*buf = b;

	return 0;
}

/*
 * Build a whole request packet.
 */

size_t
l1_command_build(u_char *buf, size_t buflen, uint32_t address, uint16_t request,
    int nargs, ...)
{
	va_list ap;
	uint32_t data;
	size_t len = 0;
	int argtype;
	const char *str;

	/*
	 * Setup packet header (type, channel, address, request)
	 */

	if (buflen >= 1) {
		*buf++ = L1PKT_REQUEST | L1CH_MISC;
		buflen--;
	}
	len++;

	if (buflen >= 4) {
		l1_packet_put_be32(buf, address);
		buf += 4;
		buflen -= 4;
	}
	len += 4;

	if (buflen >= 2) {
		l1_packet_put_be16(buf, request);
		buf += 2;
		buflen -= 2;
	}
	len += 2;

	/*
	 * Setup command arguments
	 */

	if (buflen >= 1) {
		*buf++ = nargs;
		buflen--;
	}
	len++;

	va_start(ap, nargs);
	while (nargs-- != 0) {
		argtype = va_arg(ap, int);
		switch (argtype) {
		case L1_ARG_INT:
			data = va_arg(ap, uint32_t);
			if (buflen >= 5) {
				*buf++ = L1_ARG_INT;
				l1_packet_put_be32(buf, data);
				buf += 4;
				buflen -= 5;
			}
			len += 5;
			break;
		case L1_ARG_ASCII:
			str = va_arg(ap, const char *);
			data = strlen(str);
			if (buflen >= data + 2) {
				*buf++ = L1_ARG_ASCII;
				memcpy(buf, str, data + 1);
				buf += data + 1;
				buflen -= data + 2;
			}
			len += data + 2;
			break;
		case L1_ARG_BINARY:
			data = (uint32_t)va_arg(ap, size_t);	/* size */
			str = va_arg(ap, const char *);		/* data */
			if (buflen >= 1 + data) {
				*buf++ = L1_ARG_BINARY | data;
				memcpy(buf, str, data);
				buf += data;
				buflen -= data + 1;
			}
			len += data + 1;
			break;
		}
	}
	va_end(ap);

	return len;
}

/*
 * Get a packet response, ignoring any other packet seen in between.
 * Note that despite being the only user of L1 in the kernel, we may
 * receive event packets from the console.
 */
int
l1_receive_response(int16_t nasid, u_char *pkt, size_t *pktlen)
{
	int rc;

	for (;;) {
		rc = l1_packet_get(nasid, pkt, *pktlen);
		if (rc == L1PG_TIMEOUT)
			return EWOULDBLOCK;

		if (rc < 0)	/* bad packet */
			continue;

		if (pkt[0] != (L1PKT_RESPONSE | L1CH_MISC)) {
#ifdef L1_DEBUG
			printf("unexpected L1 packet: head %02x\n", pkt[0]);
#endif
			continue;	/* it's not our response */
		}

		*pktlen = (size_t)rc;
		return 0;
	}
}

/*
 * Process a response code.
 */
int
l1_response_to_errno(uint32_t response)
{
	int rc;

	switch (response) {
	case L1_RESP_OK:
		rc = 0;
		break;
	case L1_RESP_INVAL:
		rc =  EINVAL;
		break;
	case L1_RESP_NXDATA:
		rc = ENXIO;
		break;
	default:
#ifdef L1_DEBUG
		printf("unexpected L1 response code: %08x\n", response);
#endif
		rc = EIO;
		break;
	}

	return rc;
}

/*
 * Read a board IA information record from EEPROM
 */

#define	EEPROM_CHUNK	0x40

int
l1_read_board_ia(int16_t nasid, int type, u_char **ria, size_t *rialen)
{
	u_char pkt[64 + EEPROM_CHUNK];	/* command and response packet buffer */
	u_char *pktbuf, *chunk, *ia = NULL;
	size_t pktlen, chunklen, ialen, iapos;
	uint32_t data;
	int rc;

	/*
	 * Build a first packet, asking for 0 bytes to be read.
	 */
	pktlen = l1_command_build(pkt, sizeof pkt,
	    L1_ADDRESS(type, L1_ADDRESS_LOCAL | L1_TASK_GENERAL),
	    L1_REQ_EEPROM, 4,
	    L1_ARG_INT, (uint32_t)L1_EEP_LOGIC,
	    L1_ARG_INT, (uint32_t)L1_EEP_BOARD,
	    L1_ARG_INT, (uint32_t)0,	/* offset */
	    L1_ARG_INT, (uint32_t)0);	/* size */
	if (pktlen > sizeof pkt) {
#ifdef DIAGNOSTIC
		panic("%s: L1 command packet too large (%zu) for buffer",
		    __func__, pktlen);
#endif
		return ENOMEM;
	}

	if (l1_packet_put(nasid, pkt, pktlen) != 0)
		return EWOULDBLOCK;

	pktlen = sizeof pkt;
	if (l1_receive_response(nasid, pkt, &pktlen) != 0)
		return EWOULDBLOCK;

	if (pktlen < 6) {
#ifdef L1_DEBUG
		printf("truncated response (length %zu)\n", pktlen);
#endif
		return EIO;
	}

	/*
	 * Check the response code.
	 */

	data = l1_packet_get_be32(&pkt[1]);
	rc = l1_response_to_errno(data);
	if (rc != 0)
		return rc;

	/*
	 * EEPROM read commands should return either one or two values:
	 * the first value is the size of the remaining EEPROM data, and
	 * the second value is the data read itself, if we asked for a
	 * nonzero size in the command (that size might be shorter than
	 * the data we asked for).
	 */

	if (pkt[5] != 1) {
#ifdef L1_DEBUG
		printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
		return EIO;
	}

	pktbuf = pkt + 6;
	pktlen -= 6;

	if (l1_packet_get_int(&pktbuf, &pktlen, &data) != 0) {
#ifdef L1_DEBUG
		printf("unable to parse response as integer\n");
#endif
		return EIO;
	}

	/*
	 * Now that we know the size of the IA entry, allocate memory for it.
	 */

	ialen = (size_t)data;
	ia = (u_char *)malloc(ialen, M_DEVBUF, M_NOWAIT);
	if (ia == NULL)
		return ENOMEM;

	/*
	 * Read the EEPROM contents in small chunks, so as not to keep L1
	 * busy for too long.
	 */

	iapos = 0;
	while (iapos < ialen) {
		/*
		 * Build a command packet, this time actually reading data.
		 */
		pktlen = l1_command_build(pkt, sizeof pkt,
		    L1_ADDRESS(type, L1_ADDRESS_LOCAL | L1_TASK_GENERAL),
		    L1_REQ_EEPROM, 4,
		    L1_ARG_INT, (uint32_t)L1_EEP_LOGIC,
		    L1_ARG_INT, (uint32_t)L1_EEP_BOARD,
		    L1_ARG_INT, (uint32_t)iapos,
		    L1_ARG_INT, (uint32_t)EEPROM_CHUNK);
		/* no need to check size again, it's the same size as earlier */

		if (l1_packet_put(nasid, pkt, pktlen) != 0) {
			rc = EWOULDBLOCK;
			goto fail;
		}

		pktlen = sizeof pkt;
		if (l1_receive_response(nasid, pkt, &pktlen) != 0) {
			rc = EWOULDBLOCK;
			goto fail;
		}

		if (pktlen < 6) {
#ifdef L1_DEBUG
			printf("truncated response (length %zu)\n", pktlen);
#endif
			rc = EIO;
			goto fail;
		}

		/*
		 * Check the response code.
		 */

		data = l1_packet_get_be32(&pkt[1]);
		rc = l1_response_to_errno(data);
		if (rc != 0)
			goto fail;

		if (pkt[5] != 2) {
#ifdef L1_DEBUG
			printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
			rc = EIO;
			goto fail;
		}

		pktbuf = pkt + 6;
		pktlen -= 6;

		if (l1_packet_get_int(&pktbuf, &pktlen, &data) != 0) {
#ifdef L1_DEBUG
			printf("unable to parse first response as integer\n");
#endif
			rc = EIO;
			goto fail;
		}

		if (l1_packet_get_binary(&pktbuf, &pktlen,
		    &chunk, &chunklen) != 0) {
#ifdef L1_DEBUG
			printf("unable to parse second response as binary\n");
#endif
			rc = EIO;
			goto fail;
		}

		/* should not happen, but we don't like infinite loops */
		if (chunklen == 0) {
#ifdef L1_DEBUG
			printf("read command returned 0 bytes\n");
#endif
			rc = EIO;
			goto fail;
		}

		memcpy(ia + iapos, chunk, chunklen);
		iapos += chunklen;
#ifdef L1_DEBUG
		printf("got %02zx bytes of eeprom, %zx/%zx\n",
		    chunklen, iapos, ialen);
#endif
	}

	/*
	 * Verify the checksum
	 */

	chunk = ia;
	iapos = ialen;
	data = 0;
	while (iapos-- != 0)
		data += *chunk++;
	if ((data & 0xff) != 0) {
#ifdef L1_DEBUG
		printf("wrong IA checksum\n");
#endif
		rc = EINVAL;
		goto fail;
	}

	*ria = ia;
	*rialen = ialen;
	return 0;

fail:
	if (ia != NULL)
		free(ia, M_DEVBUF, 0);
	return rc;
}

/*
 * Information Area (IA) decoding helpers
 *
 * The format of an Information Area is as follows:
 *   B format byte (00)
 *   B length in 8 byte units
 *   B language (00 = english)
 *  3B manufacturing date, minutes since 1/1/1996
 *   B type/length of manufacturer name string (up to 20 chars)
 *  #B manufacturer name
 *   B type/length of product name string (up to 14 chars)
 *  #B product name
 *   B type/length of serial number (up to 6 chars)
 *  #B serial number
 *   B type/length of part number (up to 10 chars)
 *  #B part number
 *   B FRU file id
 *   B type/length of board rev (always 0xC2)
 *  2B board revision
 *   B type/length of eeprom size field (0x01)
 *  1B size code for eeprom (02)
 *   B type/length of temp waiver field (0xC2)
 *  2B temp waiver
 *
 * and then in main boards only:
 *  G, P and Y encryption keys, each being
 *   B type/length (0x04)
 *  4B key
 *
 * and then on main boards being I-Bricks only:
 *   B type/length of mac address (as ascii string)
 * 12B mac address
 * followed by IEEE1394 configuration info, as type/length followed
 * by data again.
 *
 * A 0xC1 byte is the EOF record, and the last byte is a checksum.
 *
 * Type/length encoding is done as follows:
 * bits 7-6 are the type:
 *	00 binary data
 *	01 BCD
 *	02 packed 6 bit ascii
 *	03 regular 8 bit ascii
 * bits 5-0 are the length.
 */

#define	IA_TYPE_SHIFT	6
#define	IA_TYPE_BINARY	0
#define	IA_TYPE_BCD	1
#define	IA_TYPE_PACKED	2
#define	IA_TYPE_ASCII	3
#define	IA_LENGTH_MASK	0x3f

#define	IA_TL(t,l)	(((t) << IA_TYPE_SHIFT) | (l))

#define	IA_EOF		IA_TL(IA_TYPE_ASCII, 1)

static inline size_t
ia_skip(u_char *ia, size_t iapos, size_t ialen)
{
	size_t npos;

	ia += iapos;

	/* don't go past EOF marker */
	if (*ia == IA_EOF)
		return iapos;

	npos = iapos + 1 + (*ia & IA_LENGTH_MASK);
	return npos >= ialen ? iapos : npos;
}

int
l1_get_brick_ethernet_address(int16_t nasid, uint8_t *enaddr)
{
	u_char *ia;
	size_t iapos, ialen;
	char hexaddr[18], *d, *s;
	int type;
	int rc;

	/*
	 * If we are running on a C-Brick, the Ethernet address is stored
	 * in the matching I-Brick.
	 */
	if (sys_config.system_subtype == IP35_CBRICK)
		type = L1_TYPE_IOBRICK;
	else
		type = L1_TYPE_L1;

	/* read the Board IA of this node */
	rc = l1_read_board_ia(nasid, type, &ia, &ialen);
	if (rc != 0)
		return rc;

	/* simple sanity checks */
	if (ia[0] != 0 || ia[1] < 2 || ia[1] * 8UL > ialen) {
		rc = EINVAL;
		goto out;
	}

	/* skip fixed part */
	iapos = 6;
	/* skip 4 records */
	iapos = ia_skip(ia, iapos, ialen);
	iapos = ia_skip(ia, iapos, ialen);
	iapos = ia_skip(ia, iapos, ialen);
	iapos = ia_skip(ia, iapos, ialen);
	/* skip FRU */
	if (iapos < ialen - 1)
		iapos++;
	/* skip 3 records */
	iapos = ia_skip(ia, iapos, ialen);
	iapos = ia_skip(ia, iapos, ialen);
	iapos = ia_skip(ia, iapos, ialen);
	/* skip encryption key records if applicable */
	if (iapos < ialen && ia[iapos] == IA_TL(IA_TYPE_BINARY, 4)) {
		iapos = ia_skip(ia, iapos, ialen);
		iapos = ia_skip(ia, iapos, ialen);
		iapos = ia_skip(ia, iapos, ialen);
	}
	/* check the next record looks like an Ethernet address */
	if (iapos >= ialen - 1 - 12 || ia[iapos] != IA_TL(IA_TYPE_ASCII, 12)) {
		rc = EINVAL;
		goto out;
	}

	iapos++;
	s = (char *)ia + iapos;
	d = hexaddr;
	*d++ = *s++; *d++ = *s++; *d++ = ':';
	*d++ = *s++; *d++ = *s++; *d++ = ':';
	*d++ = *s++; *d++ = *s++; *d++ = ':';
	*d++ = *s++; *d++ = *s++; *d++ = ':';
	*d++ = *s++; *d++ = *s++; *d++ = ':';
	*d++ = *s++; *d++ = *s++; *d++ = '\0';
	enaddr_aton(hexaddr, enaddr);

out:
	free(ia, M_DEVBUF, 0);
	return rc;
}

/*
 * Issue an arbitrary L1 command.
 */
int
l1_exec_command(int16_t nasid, const char *cmd)
{
	u_char pkt[64 + 64];	/* command and response packet buffer */
	size_t pktlen;
	uint32_t data;
	int rc;

	/*
	 * Build the command packet.
	 */
	pktlen = l1_command_build(pkt, sizeof pkt,
	    L1_ADDRESS(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_COMMAND),
	    L1_REQ_EXEC_CMD, 1,
	    L1_ARG_ASCII, cmd);
	if (pktlen > sizeof pkt) {
#ifdef DIAGNOSTIC
		panic("%s: L1 command packet too large (%zu) for buffer",
		    __func__, pktlen);
#endif
		return ENOMEM;
	}

	if (l1_packet_put(nasid, pkt, pktlen) != 0)
		return EWOULDBLOCK;

	pktlen = sizeof pkt;
	if (l1_receive_response(nasid, pkt, &pktlen) != 0)
		return EWOULDBLOCK;

	if (pktlen < 6) {
#ifdef L1_DEBUG
		printf("truncated response (length %zu)\n", pktlen);
#endif
		return EIO;
	}

	/*
	 * Check the response code.
	 */

	data = l1_packet_get_be32(&pkt[1]);
	rc = l1_response_to_errno(data);
	if (rc != 0)
		return rc;

	/*
	 * We do not expect anything in return.
	 */

	if (pkt[5] != 0) {
#ifdef L1_DEBUG
		printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
		return EIO;
	}

	return 0;
}

/*
 * Get a DIMM SPD record.
 */

int
l1_get_brick_spd_record(int16_t nasid, int dimm, u_char **rspd, size_t *rspdlen)
{
	u_char pkt[64 + EEPROM_CHUNK];	/* command and response packet buffer */
	u_char *pktbuf, *chunk, *spd = NULL;
	size_t pktlen, chunklen, spdlen, spdpos;
	uint32_t address, data;
	int rc;

	/*
	 * The L1 address of SPD records differs between Fuel and Origin 350
	 * systems. This is likely because the Fuel is a single-PIMM system,
	 * while all other IP35 are dual-PIMM, and thus carry one more PIMM
	 * record at a lower address (and are interleaving DIMM accesses).
	 * Since Fuel is also a single-node system, we can safely check for
	 * the system subtype to decide which address to use.
	 */
	switch (sys_config.system_subtype) {
	case IP35_FUEL:
		address = L1_EEP_DIMM_NOINTERLEAVE(dimm);
		break;
	case IP35_CBRICK:
		address = L1_EEP_DIMM_INTERLEAVE(L1_EEP_DIMM_BASE_CBRICK, dimm);
		break;
	default:
		address =
		    L1_EEP_DIMM_INTERLEAVE(L1_EEP_DIMM_BASE_CHIMERA, dimm);
		break;
	}

	/*
	 * Build a first packet, asking for 0 bytes to be read.
	 */
	pktlen = l1_command_build(pkt, sizeof pkt,
	    L1_ADDRESS(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_GENERAL),
	    L1_REQ_EEPROM, 4,
	    L1_ARG_INT, address,
	    L1_ARG_INT, (uint32_t)L1_EEP_SPD,
	    L1_ARG_INT, (uint32_t)0,	/* offset */
	    L1_ARG_INT, (uint32_t)0);	/* size */
	if (pktlen > sizeof pkt) {
#ifdef DIAGNOSTIC
		panic("%s: L1 command packet too large (%zu) for buffer",
		    __func__, pktlen);
#endif
		return ENOMEM;
	}

	if (l1_packet_put(nasid, pkt, pktlen) != 0)
		return EWOULDBLOCK;

	pktlen = sizeof pkt;
	if (l1_receive_response(nasid, pkt, &pktlen) != 0)
		return EWOULDBLOCK;

	if (pktlen < 6) {
#ifdef L1_DEBUG
		printf("truncated response (length %zu)\n", pktlen);
#endif
		return EIO;
	}

	/*
	 * Check the response code.
	 */

	data = l1_packet_get_be32(&pkt[1]);
	rc = l1_response_to_errno(data);
	if (rc != 0)
		return rc;

	/*
	 * EEPROM read commands should return either one or two values:
	 * the first value is the size of the remaining EEPROM data, and
	 * the second value is the data read itself, if we asked for a
	 * nonzero size in the command (that size might be shorter than
	 * the data we asked for).
	 */

	if (pkt[5] != 1) {
#ifdef L1_DEBUG
		printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
		return EIO;
	}

	pktbuf = pkt + 6;
	pktlen -= 6;

	if (l1_packet_get_int(&pktbuf, &pktlen, &data) != 0) {
#ifdef L1_DEBUG
		printf("unable to parse response as integer\n");
#endif
		return EIO;
	}

	/*
	 * Now that we know the size of the spd record, allocate memory for it.
	 */

	spdlen = (size_t)data;
	spd = (u_char *)malloc(spdlen, M_DEVBUF, M_NOWAIT);
	if (spd == NULL)
		return ENOMEM;

	/*
	 * Read the EEPROM contents in small chunks, so as not to keep L1
	 * busy for too long.
	 */

	spdpos = 0;
	while (spdpos < spdlen) {
		/*
		 * Build a command packet, this time actually reading data.
		 */
		pktlen = l1_command_build(pkt, sizeof pkt,
		    L1_ADDRESS(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_GENERAL),
		    L1_REQ_EEPROM, 4,
		    L1_ARG_INT, address,
		    L1_ARG_INT, (uint32_t)L1_EEP_SPD,
		    L1_ARG_INT, (uint32_t)spdpos,
		    L1_ARG_INT, (uint32_t)EEPROM_CHUNK);
		/* no need to check size again, it's the same size as earlier */

		if (l1_packet_put(nasid, pkt, pktlen) != 0) {
			rc = EWOULDBLOCK;
			goto fail;
		}

		pktlen = sizeof pkt;
		if (l1_receive_response(nasid, pkt, &pktlen) != 0) {
			rc = EWOULDBLOCK;
			goto fail;
		}

		if (pktlen < 6) {
#ifdef L1_DEBUG
			printf("truncated response (length %zu)\n", pktlen);
#endif
			rc = EIO;
			goto fail;
		}

		/*
		 * Check the response code.
		 */

		data = l1_packet_get_be32(&pkt[1]);
		rc = l1_response_to_errno(data);
		if (rc != 0)
			goto fail;

		if (pkt[5] != 2) {
#ifdef L1_DEBUG
			printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
			rc = EIO;
			goto fail;
		}

		pktbuf = pkt + 6;
		pktlen -= 6;

		if (l1_packet_get_int(&pktbuf, &pktlen, &data) != 0) {
#ifdef L1_DEBUG
			printf("unable to parse first response as integer\n");
#endif
			rc = EIO;
			goto fail;
		}

		if (l1_packet_get_binary(&pktbuf, &pktlen,
		    &chunk, &chunklen) != 0) {
#ifdef L1_DEBUG
			printf("unable to parse second response as binary\n");
#endif
			rc = EIO;
			goto fail;
		}

		/* should not happen, but we don't like infinite loops */
		if (chunklen == 0) {
#ifdef L1_DEBUG
			printf("read command returned 0 bytes\n");
#endif
			rc = EIO;
			goto fail;
		}

		memcpy(spd + spdpos, chunk, chunklen);
		spdpos += chunklen;
#ifdef L1_DEBUG
		printf("got %02zx bytes of eeprom, %zx/%zx\n",
		    chunklen, spdpos, spdlen);
#endif
	}

	*rspd = spd;
	*rspdlen = spdlen;
	return 0;

fail:
	if (spd != NULL)
		free(spd, M_DEVBUF, 0);
	return rc;
}

int
l1_display(int16_t nasid, int down, const char *message)
{
	char line[1 + 12];
	u_char pkt[64];	/* command packet buffer */
	size_t pktlen;
	uint32_t data;
	int rc;

	if (sys_config.system_subtype == IP35_FUEL)
		return ENXIO;

	/* pad the line with spaces and truncate at 12 chars */
	(void)snprintf(line, sizeof line, "%s            ", message);

	/*
	 * Build the command packet.
	 */
	pktlen = l1_command_build(pkt, sizeof pkt,
	    L1_ADDRESS(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_GENERAL),
	    down ? L1_REQ_DISP2 : L1_REQ_DISP1, 1,
	    L1_ARG_ASCII, line);
	if (pktlen > sizeof pkt) {
#ifdef DIAGNOSTIC
		panic("%s: L1 command packet too large (%zu) for buffer",
		    __func__, pktlen);
#endif
		return ENOMEM;
	}

	if (l1_packet_put(nasid, pkt, pktlen) != 0)
		return EWOULDBLOCK;

	pktlen = sizeof pkt;
	if (l1_receive_response(nasid, pkt, &pktlen) != 0)
		return EWOULDBLOCK;

	if (pktlen < 6) {
#ifdef L1_DEBUG
		printf("truncated response (length %zu)\n", pktlen);
#endif
		return EIO;
	}

	/*
	 * Check the response code.
	 */

	data = l1_packet_get_be32(&pkt[1]);
	rc = l1_response_to_errno(data);
	if (rc != 0)
		return rc;

	/*
	 * We do not expect anything in return.
	 */

	if (pkt[5] != 0) {
#ifdef L1_DEBUG
		printf("unexpected L1 response: %d values\n", pkt[5]);
#endif
		return EIO;
	}

	return 0;
}


code:
/*	$OpenBSD: l1.h,v 1.6 2014/11/25 19:08:42 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * High-level L1 communication defines
 */

/* L1 command types and destination addresses */
#define	L1_ADDRESS(type,addr)	(((type) << 28) | (addr))

#define	L1_TYPE_L1	0x00
#define	L1_TYPE_L2	0x01
#define	L1_TYPE_L3	0x02
#define	L1_TYPE_CBRICK	0x03
#define	L1_TYPE_IOBRICK	0x04

#define	L1_ADDRESS_RACK_MASK	0x0ffc0000
#define	L1_ADDRESS_RACK_SHIFT		18
#define	L1_ADDRESS_RACK_LOCAL		0x3ff
#define	L1_ADDRESS_BAY_MASK	0x0003f000
#define	L1_ADDRESS_BAY_SHIFT		12
#define	L1_ADDRESS_BAY_LOCAL		0x3f
#define	L1_ADDRESS_TASK_MASK	0x0000001f
#define	L1_ADDRESS_TASK_SHIFT		0

#define	L1_ADDRESS_LOCAL \
	((L1_ADDRESS_RACK_LOCAL << L1_ADDRESS_RACK_SHIFT) | \
	 (L1_ADDRESS_BAY_LOCAL << L1_ADDRESS_BAY_SHIFT))

#define	L1_TASK_INVALID		0x00
#define	L1_TASK_ROUTER		0x01
#define	L1_TASK_SYSMGMT		0x02
#define	L1_TASK_COMMAND		0x03
#define	L1_TASK_ENVIRONMENT	0x04
#define	L1_TASK_BEDROCK		0x05
#define	L1_TASK_GENERAL		0x06

/* response codes */
#define	L1_RESP_OK		((uint32_t)0)
#define	L1_RESP_NXDATA		((uint32_t)-0x68)
#define	L1_RESP_INVAL		((uint32_t)-0x6b)

/*
 * Various commands (very incomplete list)
 */

/* L1_TASK_COMMAND requests */
#define	L1_REQ_EXEC_CMD	0x0000	/* interpret plaintext command */

/* L1_TASK_GENERAL requests */
#define	L1_REQ_EEPROM	0x0006	/* access eeprom */
#define	L1_REQ_DISP1	0x1004	/* display text on LCD first line */
#define	L1_REQ_DISP2	0x1005	/* display text on LCD second line */

/* L1_REQ_EEPROM additional argument value */
/* non C-brick component */
#define	L1_EEP_POWER	0x00		/* power board */
#define	L1_EEP_LOGIC	0x01		/* logic board */
/* C-brick component */
#define	L1_EEP_DIMM_NOINTERLEAVE_BASE	0x04
#define	L1_EEP_DIMM_BASE_CBRICK		0x03
#define	L1_EEP_DIMM_BASE_CHIMERA	0x05
#define	L1_EEP_DIMM_NOINTERLEAVE(dimm) \
	(L1_EEP_DIMM_NOINTERLEAVE_BASE + (dimm))
#define	L1_EEP_DIMM_INTERLEAVE(base, dimm) \
	((base) + ((dimm) >> 1) + ((dimm) & 0x01 ? 4 : 0))
/* ia code */
#define	L1_EEP_CHASSIS	0x01		/* chassis ia */
#define	L1_EEP_BOARD	0x02		/* board ia */
#define	L1_EEP_IUSE	0x03		/* internal use ia */
#define	L1_EEP_SPD	0x04		/* spd record */

#define	L1_SPD_DIMM_MAX	8

struct spdmem_attach_args {
	struct mainbus_attach_args	maa;
	int				dimm;
};

int	l1_display(int16_t, int, const char *);
int	l1_exec_command(int16_t, const char *);
int	l1_get_brick_ethernet_address(int16_t, uint8_t *);
int	l1_get_brick_spd_record(int16_t, int, u_char **, size_t *);

license: unknown


code:
/*	$OpenBSD: lock_machdep.c,v 1.7 2015/02/11 07:05:39 dlg Exp $	*/

/*
 * Copyright (c) 2007 Artur Grabowski <art@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>

#include <machine/cpu.h>
#include <machine/lock.h>

#include <ddb/db_output.h>

void
__mp_lock_init(struct __mp_lock *lock)
{
	lock->mpl_cpu = NULL;
	lock->mpl_count = 0;
}

#if defined(MP_LOCKDEBUG)
#ifndef DDB
#error "MP_LOCKDEBUG requires DDB"
#endif

/* CPU-dependent timing, needs this to be settable from ddb. */
extern int __mp_lock_spinout;
#endif

#define SPINLOCK_SPIN_HOOK	/**/

static __inline void
__mp_lock_spin(struct __mp_lock *mpl)
{
#ifndef MP_LOCKDEBUG
	while (mpl->mpl_count != 0)
		SPINLOCK_SPIN_HOOK;
#else
	int ticks = __mp_lock_spinout;

	while (mpl->mpl_count != 0 && --ticks > 0)
		SPINLOCK_SPIN_HOOK;

	if (ticks == 0) {
		db_printf("__mp_lock(0x%x): lock spun out", mpl);
		Debugger();
	}
#endif
}

void
__mp_lock(struct __mp_lock *mpl)
{
	register_t sr;
	struct cpu_info *ci = curcpu();

	/*
	 * Please notice that mpl_count gets incremented twice for the
	 * first lock. This is on purpose. The way we release the lock
	 * in mp_unlock is to decrement the mpl_count and then check if
	 * the lock should be released. Since mpl_count is what we're
	 * spinning on, decrementing it in mpl_unlock to 0 means that
	 * we can't clear mpl_cpu, because we're no longer holding the
	 * lock. In theory mpl_cpu doesn't need to be cleared, but it's
	 * safer to clear it and besides, setting mpl_count to 2 on the
	 * first lock makes most of this code much simpler.
	 */
	while (1) {
		sr = disableintr();
		if (__cpu_cas(&mpl->mpl_count, 0, 1) == 0) {
			mips_sync();
			mpl->mpl_cpu = ci;
		}

		if (mpl->mpl_cpu == ci) {
			mpl->mpl_count++;
			setsr(sr);
			break;
		}
		setsr(sr);

		__mp_lock_spin(mpl);
	}
}

void
__mp_unlock(struct __mp_lock *mpl)
{
	register_t sr;

#ifdef MP_LOCKDEBUG
	if (mpl->mpl_cpu != curcpu()) {
		db_printf("__mp_unlock(%p): not held lock\n", mpl);
		Debugger();
	}
#endif

	sr = disableintr();
	if (--mpl->mpl_count == 1) {
		mpl->mpl_cpu = NULL;
		mips_sync();
		mpl->mpl_count = 0;
	}

	setsr(sr);
}

int
__mp_release_all(struct __mp_lock *mpl)
{
	int rv = mpl->mpl_count - 1;
	register_t sr;

#ifdef MP_LOCKDEBUG
	if (mpl->mpl_cpu != curcpu()) {
		db_printf("__mp_release_all(%p): not held lock\n", mpl);
		Debugger();
	}
#endif

	sr = disableintr();
	mpl->mpl_cpu = NULL;
	mips_sync();
	mpl->mpl_count = 0;
	setsr(sr);

	return (rv);
}

int
__mp_release_all_but_one(struct __mp_lock *mpl)
{
	int rv = mpl->mpl_count - 2;
#ifdef MP_LOCKDEBUG
	if (mpl->mpl_cpu != curcpu()) {
		db_printf("__mp_release_all_but_one(%p): not held lock\n", mpl);
		Debugger();
	}
#endif

	mpl->mpl_count = 2;

	return (rv);
}

void
__mp_acquire_count(struct __mp_lock *mpl, int count)
{
	while (count--)
		__mp_lock(mpl);
}

int
__mp_lock_held(struct __mp_lock *mpl)
{
	return mpl->mpl_cpu == curcpu();
}


code:
/*	$OpenBSD: locore.S,v 1.15 2012/10/03 11:18:23 miod Exp $ */

/*
 * Copyright (c) 2001-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/errno.h>
#include <sys/syscall.h>

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/regnum.h>
#include <machine/cpustate.h>

#include "assym.h"

	.set	mips3
	.set	noreorder		# Noreorder is default style!

	.globl	start
	.globl	kernel_text
kernel_text = start
start:
	/*
	 * On at least the O2, when netbooting the bsd.rd kernel, the
	 * kernel image gets loaded in CKSEG1, which causes the kernel
	 * text to be uncached.  Just to be on the safe side, jump to
	 * our intended execution address.
	 */
	LA	v0, 1f
	jr	v0
	 NOP
1:

	MFC0	v0, COP_0_STATUS_REG
	LI	v1, ~SR_INT_ENAB
	and	v0, v1
	MTC0	v0, COP_0_STATUS_REG		# disable all interrupts
	MTC0_SR_IE_HAZARD
	MTC0	zero, COP_0_CAUSE_REG		# Clear soft interrupts
	MTC0_HAZARD

	/*
	 * Initialize stack and call machine startup.
	 */
	LA	sp, start - FRAMESZ(CF_SZ)
	LA	gp, _gp
	jal	mips_init			# mips_init(argc, argv, envp)
	PTR_S	zero, CF_RA_OFFS(sp)		# Zero out old ra for debugger

	move	sp, v0				# switch to new stack
	jal	main				# main(regs)
	 move	a0, zero
	PANIC("Startup failed!")

#if defined(MULTIPROCESSOR)
LEAF(hw_cpu_spinup_trampoline, 0)
	MFC0	v0, COP_0_STATUS_REG
	LI	v1, ~SR_INT_ENAB
	and	v0, v1
	ori	v0, SR_KX | SR_UX
	MTC0	v0, COP_0_STATUS_REG		# disable all interrupts
	MTC0	zero, COP_0_CAUSE_REG		# Clear soft interrupts
	LA	gp, _gp
	jal	hw_cpu_hatch
	 NOP
END(hw_cpu_spinup_trampoline)

#ifdef TGT_OCTANE

LEAF(hw_getcurcpu, 0)
	GET_CPU_INFO(v0, v1)
	j	ra
	 NOP
END(hw_getcurcpu)

/*
 * There does not seem to be any fast way to store the curcpu pointer on
 * Octane. We used to abuse the LLAddr register, but this is a 32-bit register,
 * not suitable for allocation over 2GB physical.
 * Instead, we will use an unused field of the MPCONF structure.
 */
LEAF(hw_setcurcpu, 0)
	HW_GET_CPU_PRID(v0, v1)			# get physical processor ID
	LOAD_XKPHYS(v1, CCA_COHERENT_EXCLWRITE)
	PTR_SLL	v0, MPCONF_SHIFT
	PTR_ADD	v0, MPCONF_BASE
	or	v1, v0
	j	ra
	 PTR_S	a0, (MPCONF_LEN - REGSZ)(v1)
END(hw_setcurcpu)
#endif
#endif /* MULTIPROCESSOR */


code:
/*	$OpenBSD: machdep.c,v 1.150 2014/12/10 15:29:53 mikeb 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/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/msgbuf.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>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#ifdef SYSVSEM
#include <sys/sem.h>
#endif

#include <net/if.h>

#include <uvm/uvm_extern.h>

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

#include <mips64/cache.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/frame.h>
#include <machine/autoconf.h>
#include <machine/memconf.h>
#include <machine/regnum.h>
#ifdef TGT_ORIGIN
#include <machine/mnode.h>
#endif
#if defined(TGT_INDY) || defined(TGT_INDIGO2)
CACHE_PROTOS(ip22)
#endif
#ifdef TGT_INDIGO2
CACHE_PROTOS(tcc)
#endif

#ifdef CPU_RM7000
#include <mips64/rm7000.h>
#endif

#include <dev/cons.h>

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

extern char kernel_text[];
extern bus_addr_t comconsaddr;

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

/* will be updated in ipXX_machdep.c whenever necessary */
struct uvm_constraint_range  dma_constraint = { 0x0, (paddr_t)-1 };
struct uvm_constraint_range *uvm_md_constraints[] = {
	&dma_constraint,
	NULL
};

vm_map_t exec_map;
vm_map_t phys_map;

/*
 * safepri is a safe priority for sleep to set for a spin-wait
 * during autoconfiguration or after a panic.
 */
int   safepri = 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;
int	console_ok;		/* Set when console initialized. */
int16_t	masternasid;

int32_t *environment;
struct sys_rec sys_config;
struct cpu_hwinfo bootcpu_hwinfo;

/* Pointers to the start and end of the symbol table. */
caddr_t	ssym;
caddr_t	esym;
caddr_t	ekern;

struct phys_mem_desc mem_layout[MAXMEMSEGS];

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

static void dobootopts(int, void *);

boolean_t is_memory_range(paddr_t, psize_t, psize_t);

void	(*md_halt)(int) = arcbios_halt;

int sgi_cpuspeed(int *);

/*
 * 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, caddr_t boot_esym)
{
	char *cp;
	int i, guessed;
	u_int cpufamily;
	struct cpu_info *ci;
	extern char start[], edata[], end[];
	extern char *hw_vendor;
#ifndef CPU_R8000
	extern char cache_err[], exception[], e_exception[];
	vaddr_t xtlb_handler;
#endif

#ifdef MULTIPROCESSOR
	/*
	 * Set curcpu address on primary processor.
	 */
	setcurcpu(&cpu_info_primary);
#endif

	ci = curcpu();

	/*
	 * 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 space for the symbol table, if it exists.
	 */
	ssym = (char *)*(u_int64_t *)end;

	/* Attempt to locate ELF header and symbol table after kernel. */
	if (end[0] == ELFMAG0 && end[1] == ELFMAG1 &&
	    end[2] == ELFMAG2 && end[3] == ELFMAG3 ) {

		/* ELF header exists directly after kernel. */
		ssym = end;
		esym = boot_esym;
		ekern = esym;

	} else if (((long)ssym - (long)end) >= 0 &&
	    ((long)ssym - (long)end) <= 0x1000 &&
	    ssym[0] == ELFMAG0 && ssym[1] == ELFMAG1 &&
	    ssym[2] == ELFMAG2 && ssym[3] == ELFMAG3 ) {

		/* Pointers exist directly after kernel. */
		esym = (char *)*((u_int64_t *)end + 1);
		ekern = esym;

	} else {

		/* Pointers aren't setup either... */
		ssym = NULL;
		esym = NULL;
		ekern = end;
	}

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

	/*
	 * Read and store ARCBios variables for future reference.
	 */
	cp = Bios_GetEnvironmentVariable("ConsoleOut");
	if (cp != NULL && *cp != '\0')
		strlcpy(bios_console, cp, sizeof(bios_console));
	cp = Bios_GetEnvironmentVariable("gfx");
	if (cp != NULL && *cp != '\0')
		strlcpy(bios_graphics, cp, sizeof(bios_graphics));
	cp = Bios_GetEnvironmentVariable("keybd");
	if (cp != NULL && *cp != '\0')
		strlcpy(bios_keyboard, cp, sizeof(bios_keyboard));
	/* the following variables may not exist on all systems */
	cp = Bios_GetEnvironmentVariable("eaddr");
	if (cp != NULL && strlen(cp) > 0)
		strlcpy(bios_enaddr, cp, sizeof bios_enaddr);
	bios_consrate = bios_getenvint("dbaud");
	if (bios_consrate < 50 || bios_consrate > 115200)
		bios_consrate = 9600;	/* sane default */
	cp = Bios_GetEnvironmentVariable("OSLoadOptions");
	if (cp != NULL && strlen(cp) > 0)
		strlcpy(osloadoptions, cp, sizeof osloadoptions);

	/*
	 * Determine system type and set up configuration record data.
	 */
	hw_vendor = "SGI";
	switch (sys_config.system_type) {
#ifdef TGT_INDIGO
	case SGI_IP20:
		bios_printf("Found SGI-IP20, setting up.\n");
		/* IP22 is intentional, we use the same kernel */
		strlcpy(cpu_model, "IP22", sizeof(cpu_model));
		ip22_setup();
		break;
#endif
#if defined(TGT_INDY) || defined(TGT_INDIGO2)
	case SGI_IP22:
		bios_printf("Found SGI-IP22, setting up.\n");
		strlcpy(cpu_model, "IP22", sizeof(cpu_model));
		ip22_setup();
		break;
#endif
#ifdef TGT_INDIGO2
	case SGI_IP26:
		bios_printf("Found SGI-IP26, setting up.\n");
		strlcpy(cpu_model, "IP26", sizeof(cpu_model));
		ip22_setup();
		break;
	case SGI_IP28:
		bios_printf("Found SGI-IP28, setting up.\n");
		strlcpy(cpu_model, "IP28", sizeof(cpu_model));
		ip22_setup();
		break;
#endif
#ifdef TGT_O2
	case SGI_O2:
		bios_printf("Found SGI-IP32, setting up.\n");
		strlcpy(cpu_model, "IP32", sizeof(cpu_model));
		ip32_setup();
		break;
#endif
#ifdef TGT_ORIGIN
	case SGI_IP27:
		bios_printf("Found SGI-IP27, setting up.\n");
		strlcpy(cpu_model, "IP27", sizeof(cpu_model));
		ip27_setup();

		break;

	case SGI_IP35:
		bios_printf("Found SGI-IP35, setting up.\n");
		/* IP27 is intentional, we use the same kernel */
		strlcpy(cpu_model, "IP27", sizeof(cpu_model));
		ip27_setup();

		break;
#endif
#ifdef TGT_OCTANE
	case SGI_OCTANE:
		bios_printf("Found SGI-IP30, setting up.\n");
		strlcpy(cpu_model, "IP30", sizeof(cpu_model));
		ip30_setup();
		break;
#endif
	default:
		bios_printf("There is no support for this system type "
		    "(%02x) in this kernel.\n"
		    "Are you sure you have booted the right kernel "
		    "for this machine?\n",
		    sys_config.system_type);
		bios_printf("Halting system.\n");
		Bios_Halt();
		for (;;) ;
	}

	/*
	 * Look at arguments passed to us and compute boothowto.
	 */
	boothowto = RB_AUTOBOOT;
	dobootopts(argc, argv);

	/*
	 * Figure out where we supposedly booted from.
	 */
	cp = Bios_GetEnvironmentVariable("OSLoadPartition");
	if (cp == NULL)
		cp = "unknown";
	if (strlcpy(osloadpartition, cp, sizeof osloadpartition) >=
	    sizeof osloadpartition)
		bios_printf("Value of `OSLoadPartition' is too large.\n"
		 "The kernel might not be able to find out its root device.\n");

	/*
	 * 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_last_page != 0; i++) {
		uint64_t fp, lp;
		uint64_t firstkernpage, lastkernpage;
		paddr_t firstkernpa, lastkernpa;

		if (IS_XKPHYS((vaddr_t)start))
			firstkernpa = XKPHYS_TO_PHYS((vaddr_t)start);
		else
			firstkernpa = CKSEG0_TO_PHYS((vaddr_t)start);
		if (IS_XKPHYS((vaddr_t)ekern))
			lastkernpa = XKPHYS_TO_PHYS((vaddr_t)ekern);
		else
			lastkernpa = CKSEG0_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, 0);
			continue;	/* Outside kernel. */
		}

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

	/*
	 * Configure cache.
	 */
	guessed = 0;
	switch (bootcpu_hwinfo.type) {
#ifdef CPU_R4000
	case MIPS_R4000:
		cpufamily = MIPS_R4000;
		break;
#endif
#ifdef CPU_R4600
	case MIPS_R4600:
		cpufamily = MIPS_R5000;
		break;
#endif
#ifdef CPU_R5000
	case MIPS_R5000:
	case MIPS_RM52X0:
		cpufamily = MIPS_R5000;
		break;
#endif
#ifdef CPU_RM7000
	case MIPS_RM7000:
	case MIPS_RM9000:
		cpufamily = MIPS_R5000;
		break;
#endif
#ifdef CPU_R8000
	case MIPS_R8000:
		cpufamily = MIPS_R8000;
		break;
#endif
#ifdef CPU_R10000
	case MIPS_R10000:
	case MIPS_R12000:
	case MIPS_R14000:
		cpufamily = MIPS_R10000;
		break;
#endif
	default:
		/*
		 * If we can't identify the cpu type, it must be
		 * r10k-compatible on Octane and Origin families, and
		 * it is likely to be r5k-compatible on O2 and
		 * r4k-compatible on Ind{igo*,y}.
		 */
		guessed = 1;
		switch (sys_config.system_type) {
		case SGI_IP20:
		case SGI_IP22:
			bios_printf("Unrecognized processor type %02x, assuming"
			    " R4000 compatible\n", bootcpu_hwinfo.type);
			cpufamily = MIPS_R4000;
			break;
		case SGI_O2:
			bios_printf("Unrecognized processor type %02x, assuming"
			    " R5000 compatible\n", bootcpu_hwinfo.type);
			cpufamily = MIPS_R5000;
			break;
		case SGI_IP26:
			bios_printf("Unrecognized processor type %02x, assuming"
			    " R8000 compatible\n", bootcpu_hwinfo.type);
			cpufamily = MIPS_R8000;
			break;
		default:
		case SGI_IP27:
		case SGI_IP28:
		case SGI_OCTANE:
		case SGI_IP35:
			bios_printf("Unrecognized processor type %02x, assuming"
			    " R10000 compatible\n", bootcpu_hwinfo.type);
			cpufamily = MIPS_R10000;
			break;
		}
		break;
	}
	switch (cpufamily) {
#ifdef CPU_R4000
	case MIPS_R4000:
		Mips4k_ConfigCache(ci);
		break;
#endif
#if defined(CPU_R4600) || defined(CPU_R5000) || defined(CPU_RM7000)
	case MIPS_R5000:
#if defined(TGT_INDY) || defined(TGT_INDIGO2)
		if (sys_config.system_type == SGI_IP22)
			ip22_ConfigCache(ci);
		else
#endif
			Mips5k_ConfigCache(ci);
		break;
#endif
#ifdef CPU_R8000
	case MIPS_R8000:
#ifdef TGT_INDIGO2
		if (sys_config.system_type == SGI_IP26)
			tcc_ConfigCache(ci);
		else
#endif
			tfp_ConfigCache(ci);
		break;
#endif
#ifdef CPU_R10000
	case MIPS_R10000:
		Mips10k_ConfigCache(ci);
		break;
#endif
	default:
		if (guessed) {
			bios_printf("There is no support for this processor "
			    "family in this kernel.\n"
			    "Are you sure you have booted the right kernel "
			    "for this machine?\n");
		} else {
			bios_printf("There is no support for this processor "
			    "family (%02x) in this kernel.\n",
			    bootcpu_hwinfo.type);
		}
		bios_printf("Halting system.\n");
		Bios_Halt();
		for (;;) ;
		break;
	}

	/*
	 * Last chance to call the BIOS. Wiping the TLB means the BIOS' data
	 * areas are demapped on most systems.
	 */
	delay(20*1000);		/* Let any UART FIFO drain... */

	tlb_init(bootcpu_hwinfo.tlbsize);

#ifdef CPU_R8000	/* { */
	/*
	 * Set up TrapBase to point to our own trap vector area.
	 */
	{
		extern char tfp_trapbase[];
		cp0_set_trapbase((vaddr_t)tfp_trapbase);
	}
#else	/* } { */
	/*
	 * Copy down exception vector code.
	 */
	bcopy(exception, (char *)CACHE_ERR_EXC_VEC, e_exception - cache_err);
	bcopy(exception, (char *)GEN_EXC_VEC, e_exception - exception);

	/*
	 * Build proper TLB refill handler trampolines.
	 */
	switch (cpufamily) {
#ifdef CPU_R4000
	case MIPS_R4000:
	    {
		extern void xtlb_miss_err_r4k;
		extern void xtlb_miss_err_r4000SC;

		if (ci->ci_l2.size == 0 ||
		    ((cp0_get_prid() >> 4) & 0x0f) >= 4) /* R4400 */
			xtlb_handler = (vaddr_t)&xtlb_miss_err_r4k;
		else {
			xtlb_handler = (vaddr_t)&xtlb_miss_err_r4000SC;
			xtlb_handler |= CKSEG1_BASE;
		}
	    }
		break;
#endif
#if defined(CPU_R5000) || defined(CPU_RM7000)
	case MIPS_R5000:
	    {
		/*
		 * R5000 processors need a specific chip bug workaround
		 * in their tlb handlers.  Theoretically only revision 1
		 * of the processor need it, but there is evidence
		 * later versions also need it.
		 *
		 * This is also necessary on RM52x0 and most RM7k/RM9k,
		 * and is a documented errata for these chips.
		 */
		extern void xtlb_miss_err_r5k;
		xtlb_handler = (vaddr_t)&xtlb_miss_err_r5k;
	    }
		break;
#endif
	default:
	    {
		extern void xtlb_miss;
		xtlb_handler = (vaddr_t)&xtlb_miss;
	    }
		break;
	}

	build_trampoline(TLB_MISS_EXC_VEC, xtlb_handler);
	build_trampoline(XTLB_MISS_EXC_VEC, xtlb_handler);
#endif	/* } */

#ifdef CPU_R4000
	/*
	 * Enable R4000 EOP errata workaround code if necessary.
	 */
	if (cpufamily == MIPS_R4000 && ((cp0_get_prid() >> 4) & 0x0f) < 3)
		r4000_errata = 1;
#endif

	/*
	 * Allocate U page(s) for proc[0], pm_tlbpid 1.
	 */
	ci->ci_curproc = &proc0;
	proc0.p_cpu = ci;
	proc0.p_addr = proc0paddr = ci->ci_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(MIN_USER_ASID);

	/*
	 * Get a console, very early but after initial mapping setup
	 * and exception handler setup - console probe code might need
	 * to invoke guarded_read(), and this needs our handlers to be
	 * available.
	 */
	consinit();
	printf("Initial setup done, switching console.\n");

#ifdef DDB
	{
		/*
		 * Early initialize cpu0 so that commands such as `mach tlb'
		 * can work from ddb if ddb is entered before cpu0 attaches.
		 */
		extern struct cpu_info cpu_info_primary;
		bcopy(&bootcpu_hwinfo, &cpu_info_primary.ci_hw,
		    sizeof(struct cpu_hwinfo));
	}
#endif

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

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

#ifdef CPU_R8000
	/*
	 * Turn on precise FPU exceptions. This also causes the FS bit in
	 * the FPU status register to be honoured, instead of being forced
	 * to one.
	 */
	setsr(getsr() | SR_SERIALIZE_FPU);

	/*
	 * Turn on sequential memory model. This makes sure that there are
	 * no risks of hitting virtual coherency exceptions, which are not
	 * recoverable on R8000.
	 */
	cp0_set_config((cp0_get_config() & ~CFGR_ICE) | CFGR_SMM);
#else
	/*
	 * Turn off bootstrap exception vectors.
	 */
	setsr(getsr() & ~SR_BOOT_EXC_VEC);
#endif

	proc0.p_md.md_regs->sr = getsr();

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

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

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

/*
 * 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)
			continue;

		/*
		 * Parse PROM options.
		 */
		if (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;
			continue;
		}

		/*
		 * Parse kernel options.
		 */
		if (*cp == '-') {
			while (*++cp != '\0')
				switch (*cp) {
				case 'a':
					boothowto |= RB_ASKNAME;
					break;
				case 'c':
					boothowto |= RB_CONFIG;
					break;
				case 'd':
					boothowto |= RB_KDB;
					break;
				case 's':
					boothowto |= RB_SINGLE;
					break;
				}
		}
	}
}

/*
 * Console initialization: called early on from mips_init(), before vm init
 * is completed.
 * 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 auto-configuration.
 */
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 = %lu (%luMB)\n", ptoa((psize_t)physmem),
	    ptoa((psize_t)physmem)/1024/1024);
	printf("rsvd mem = %lu (%luMB)\n", ptoa((psize_t)rsvdmem),
	    (ptoa((psize_t)rsvdmem) + 1023 * 1024)/1024/1024);

	/*
	 * 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 = %lu (%luMB)\n", ptoa((psize_t)uvmexp.free),
	    ptoa((psize_t)uvmexp.free)/1024/1024);

	cpu_cpuspeed = sgi_cpuspeed;

	/*
	 * 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;
	}
}

int
sgi_cpuspeed(int *freq)
{
	/*
	 * XXX assumes all CPU have the same frequency
	 */
	*freq = curcpu()->ci_hw.clock / 1000000;
	return (0);
}

int	waittime = -1;

__dead void
boot(int howto)
{
	if (curproc)
		savectx(curproc->p_addr, 0);

	if (cold) {
		if ((howto & RB_USERREQ) == 0)
			howto |= RB_HALT;
		goto haltsys;
	}

	boothowto = howto;
	if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
		waittime = 0;
		vfs_shutdown();

		if ((howto & RB_TIMEBAD) == 0) {
			resettodr();
		} else {
			printf("WARNING: not updating battery clock\n");
		}
	}
	if_downall();

	uvm_shutdown();
	splhigh();
	cold = 1;

	if ((howto & RB_DUMP) != 0)
		dumpsys();

haltsys:
	config_suspend_all(DVACT_POWERDOWN);

	if ((howto & RB_HALT) != 0) {
		if ((howto & RB_POWERDOWN) != 0)
			printf("System Power Down.\n");
		else
			printf("System Halt.\n");
	} else
		printf("System restart.\n");

	delay(1000000);
	md_halt(howto);

	printf("Failed!!! Please reset manually.\n");
	for (;;) ;
	/* NOTREACHED */
}

void
arcbios_halt(int howto)
{
	register_t sr;

	sr = disableintr();

#if 0
	/* restore ARCBios page size... */
	tlb_set_page_mask(PG_SIZE_4K);
#endif

	if (howto & RB_HALT) {
#ifdef TGT_INDIGO
		/* Indigo does not support powerdown */
		if (sys_config.system_type == SGI_IP20)
			howto &= ~RB_POWERDOWN;
#endif
		if (howto & RB_POWERDOWN) {
#ifdef TGT_INDY
			/*
			 * ARCBios needs to use the FPU on Indy during
			 * shutdown.
			 */
			if (sys_config.system_type == SGI_IP22)
				setsr(getsr() | SR_COP_1_BIT);
#endif
			Bios_PowerDown();
		} else
			Bios_EnterInteractiveMode();
	} else
		Bios_Reboot();

	setsr(sr);
}

u_long	dumpmag = 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 > atop(round_page(dbtob(nblks - dumplo))))
		dumpsize = atop(round_page(dbtob(nblks - dumplo)));
	else if (dumplo == 0)
		dumplo = nblks - btodb(ptoa(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 auto-configuration, if dump device has already
	 * configured...
	 */
	if (dumpsize == 0)
		dumpconf();
	if (dumplo < 0)
		return;
	printf("\ndumping to dev %x, offset %ld\n", dumpdev, dumplo);
	printf("dump not yet implemented\n");
#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
}

boolean_t
is_memory_range(paddr_t pa, psize_t len, psize_t limit)
{
	struct phys_mem_desc *seg;
	uint64_t fp, lp;
	int i;

	fp = atop(pa);
	lp = atop(round_page(pa + len));

	if (limit != 0 && lp > atop(limit))
		return FALSE;

	for (i = 0, seg = mem_layout; i < MAXMEMSEGS; i++, seg++)
		if (fp >= seg->mem_first_page && lp <= seg->mem_last_page)
			return TRUE;

	return FALSE;
}

#ifdef CPU_RM7000
#ifdef	RM7K_PERFCNTR
/*
 * 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;

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

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

#endif	/* RM7K_PERFCNTR */
#endif	/* CPU_RM7000 */


code:
/*	$OpenBSD: mainbus.c,v 1.13 2012/09/29 21:46:02 miod Exp $ */

/*
 * Copyright (c) 2001-2003 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/device.h>
#include <sys/reboot.h>

#include <mips64/archtype.h>
#include <machine/autoconf.h>

/* Definition of the mainbus driver. */
int	mbmatch(struct device *, void *, void *);
void	mbattach(struct device *, struct device *, void *);
int	mbprint(void *, const char *);

const struct cfattach mainbus_ca = {
	sizeof(struct device), mbmatch, mbattach
};

struct cfdriver mainbus_cd = {
	NULL, "mainbus", DV_DULL
};

int
mbmatch(struct device *parent, void *cfdata, void *aux)
{
	static int mbattached = 0;

	if (mbattached != 0)
		return 0;

	return mbattached = 1;
}

void
mbattach(struct device *parent, struct device *self, void *aux)
{
	struct cpu_attach_args caa;
	extern char *hw_prod;

	if (hw_prod != NULL)
		printf(": %s", hw_prod);
	printf("\n");

	/*
	 * On multiprocessor capable systems, delegate everything to the
	 * IP-specific code.
	 */
	switch (sys_config.system_type) {
#ifdef TGT_ORIGIN
	case SGI_IP27:
	case SGI_IP35:
		ip27_autoconf(self);
		return;
#endif
#ifdef TGT_OCTANE
	case SGI_OCTANE:
		ip30_autoconf(self);
		return;
#endif
	default:
		break;
	}

	/*
	 * On other systems, attach the CPU, its clock, and the
	 * mainbus here.
	 * XXX Would be worth doing as ipXX_autoconf() too.
	 */

	bzero(&caa, sizeof caa);
	caa.caa_maa.maa_name = "cpu";
	caa.caa_hw = &bootcpu_hwinfo;
	config_found(self, &caa, mbprint);

	switch (sys_config.system_type) {
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
	case SGI_IP20:
	case SGI_IP22:
	case SGI_IP26:
	case SGI_IP28:
		/* Interrupt Controller */
		caa.caa_maa.maa_name = "int";
		config_found(self, &caa.caa_maa, mbprint);
#ifdef TGT_INDIGO2
		if (sys_config.system_type == SGI_IP26) {
			/* Streaming Cache Controller */
			caa.caa_maa.maa_name = "tcc";
			config_found(self, &caa.caa_maa, mbprint);
		}
#endif
		/* Memory Controller */
		caa.caa_maa.maa_name = "imc";
		config_found(self, &caa.caa_maa, mbprint);

		if (md_startclock == NULL) {
			caa.caa_maa.maa_name = "clock";
			config_found(self, &caa.caa_maa, mbprint);
		}

		ip22_post_autoconf();

		break;
#endif
#ifdef TGT_O2
	case SGI_O2:
		caa.caa_maa.maa_name = "clock";
		config_found(self, &caa.caa_maa, mbprint);
		caa.caa_maa.maa_name = "macebus";
		config_found(self, &caa.caa_maa, mbprint);
		caa.caa_maa.maa_name = "gbe";
		config_found(self, &caa.caa_maa, mbprint);
		break;
#endif
	default:
		break;
	}
}

int
mbprint(void *aux, const char *pnp)
{
	if (pnp)
		return (QUIET);
	return (UNCONF);
}


code:
/*	$OpenBSD: sginode.c,v 1.31 2015/02/06 22:12:19 miod Exp $	*/
/*
 * Copyright (c) 2008, 2009, 2011 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2004 Opsycon AB.  All rights reserved.
 *
 * 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 REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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/proc.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/memconf.h>
#include <machine/param.h>
#include <machine/autoconf.h>
#include <mips64/arcbios.h>
#include <mips64/archtype.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>

#include <machine/mnode.h>
#include <sgi/xbow/hub.h>
#include <sgi/xbow/widget.h>
#include <sgi/xbow/xbow.h>
#include <sgi/xbow/xbowdevs.h>

void	kl_add_memory_ip27(int16_t, int16_t *, unsigned int);
void	kl_add_memory_ip35(int16_t, int16_t *, unsigned int);

int	kl_first_pass_board(lboard_t *, void *);
int	kl_first_pass_comp(klinfo_t *, void *);

#ifdef DEBUG
#define	DB_PRF(x)	bios_printf x
#else
#define	DB_PRF(x)
#endif

/* widget number of the XBow `hub', for each node */
int	kl_hub_widget[GDA_MAXNODES];

int	kl_n_mode = 0;
u_int	kl_n_shift = 32;
klinfo_t *kl_glass_console = NULL;

void
kl_init(int ip35)
{
	kl_config_hdr_t *cfghdr;
	uint64_t val;
	uint64_t nibase = ip35 ? HUBNIBASE_IP35 : HUBNIBASE_IP27;
	size_t gsz;

	/* will be recomputed when processing memory information */
	physmem = 0;

	cfghdr = IP27_KLCONFIG_HDR(0);
	DB_PRF(("config @%p\n", cfghdr));
	DB_PRF(("magic %p version %x\n", cfghdr->magic, cfghdr->version));
	DB_PRF(("console %p baud %d\n", cfghdr->cons_info.uart_base,
	    cfghdr->cons_info.baud));

	val = IP27_LHUB_L(nibase | HUBNI_STATUS);
	kl_n_mode = (val & NI_MORENODES) != 0;
	kl_n_shift = (ip35 ? 33 : 32) - kl_n_mode;
        bios_printf("Machine is in %c mode.\n", kl_n_mode + 'M');

	val = IP27_LHUB_L(HUBPI_REGION_PRESENT);
        DB_PRF(("Region present %p.\n", val));
	val = IP27_LHUB_L(HUBPI_CALIAS_SIZE);
        DB_PRF(("Calias size %p.\n", val));

	/*
	 * Get a grip on the global data area, and figure out how many
	 * theoretical nodes are available.
	 */

	gda = IP27_GDA(0);
	gsz = IP27_GDA_SIZE(0);
	if (gda->magic != GDA_MAGIC || gda->ver < 2) {
		masternasid = 0;
		maxnodes = 0;
	} else {
		masternasid = gda->masternasid;
		maxnodes = (gsz - offsetof(gda_t, nasid)) / sizeof(int16_t);
		if (maxnodes > GDA_MAXNODES)
			maxnodes = GDA_MAXNODES;
		/* in M mode, there can't be more than 64 nodes anyway */
		if (kl_n_mode == 0 && maxnodes > 64)
			maxnodes = 64;
	}

	/*
	 * Scan all nodes configurations to find out CPU and memory
	 * information, starting with the master node.
	 */

	kl_scan_all_nodes(KLBRD_ANY, kl_first_pass_board, &ip35);
}

/*
 * Callback routine for the initial enumeration (boards).
 */
int
kl_first_pass_board(lboard_t *boardinfo, void *arg)
{
	DB_PRF(("%cboard type %x slot %x nasid %x nic %p ncomp %d\n",
	    boardinfo->struct_type & LBOARD ? 'l' : 'r',
	    boardinfo->brd_type, boardinfo->brd_slot,
	    boardinfo->brd_nasid, boardinfo->brd_nic,
	    boardinfo->brd_numcompts));

	/*
	 * Assume the worst case of a restricted XBow, as found on
	 * Origin 200 systems. This value will be overridden should a
	 * full-blown XBow be found during component enumeration.
	 *
	 * On Origin 200, widget 0 reports itself as a Bridge, and
	 * interrupt widget is hardwired to #a (which is another facet
	 * of the bridge).
	 */
	if (kl_hub_widget[boardinfo->brd_nasid] == 0)
		kl_hub_widget[boardinfo->brd_nasid] = 0x0a;

	kl_scan_board(boardinfo, KLSTRUCT_ANY, kl_first_pass_comp, arg);

	return 0;
}

#ifdef DEBUG
static const char *klstruct_names[] = {
	"unknown component",
	"cpu",
	"hub",
	"memory",
	"xbow",
	"bridge",
	"ioc3",
	"pci",
	"vme",
	"router",
	"graphics",
	"scsi",
	"fddi",
	"mio",
	"disk",
	"tape",
	"cdrom",
	"hub uart",
	"ioc3 Ethernet",
	"ioc3 uart",
	"component type 20",
	"ioc3 keyboard",
	"rad",
	"hub tty",
	"ioc3 tty",
	"fc",
	"module serialnumber",
	"ioc3 mouse",
	"tpu",
	"gsn main board",
	"gsn aux board",
	"xthd",
	"QLogic fc",
	"firewire",
	"usb",
	"usb keyboard",
	"usb mouse",
	"dual scsi",
	"PE brick",
	"gigabit Ethernet",
	"ide",
	"ioc4",
	"ioc4 uart",
	"ioc4 tty",
	"ioc4 keyboard",
	"ioc4 mouse",
	"ioc4 ATA",
	"pci graphics"
};
#endif

/*
 * Callback routine for the initial enumeration (components).
 * We are interested in cpu and memory information only, but display a few
 * other things if option DEBUG.
 */
int
kl_first_pass_comp(klinfo_t *comp, void *arg)
{
	int ip35 = *(int *)arg;
	klcpu_t *cpucomp;
	klmembnk_m_t *memcomp_m;
	klxbow_t *xbowcomp;
	arc_config64_t *arc;
#ifdef DEBUG
	klmembnk_n_t *memcomp_n;
	klhub_t *hubcomp;
	klscsi_t *scsicomp;
	klscctl_t *scsi2comp;
	int i;
#endif

	arc = (arc_config64_t *)comp->arcs_compt;

#ifdef DEBUG
	if (comp->struct_type < nitems(klstruct_names))
		DB_PRF(("\t%s", klstruct_names[comp->struct_type]));
	else
		DB_PRF(("\tcomponent type %d", comp->struct_type));
	DB_PRF((", widget %x physid 0x%02x virtid %d",
	    comp->widid, comp->physid, comp->virtid));
	if (ip35) {
		DB_PRF((" prt %d bus %d", comp->port, comp->pci_bus_num));
		if (comp->pci_multifunc)
			DB_PRF((" pcifn %d", comp->pci_func_num));
	}
	DB_PRF(("\n"));
#endif

	switch (comp->struct_type) {
	case KLSTRUCT_CPU:
		cpucomp = (klcpu_t *)comp;
		DB_PRF(("\t  type %x/%x %dMHz cache %dMB speed %dMHz\n",
		    cpucomp->cpu_prid, cpucomp->cpu_fpirr,
		    cpucomp->cpu_speed, cpucomp->cpu_scachesz,
		    cpucomp->cpu_scachespeed));
		/*
		 * XXX this assumes the first cpu encountered is the boot
		 * XXX cpu.
		 */
		if (bootcpu_hwinfo.clock == 0) {
			bootcpu_hwinfo.c0prid = cpucomp->cpu_prid;
			bootcpu_hwinfo.c1prid = cpucomp->cpu_prid; /* XXX */
			bootcpu_hwinfo.clock = cpucomp->cpu_speed * 1000000;
			bootcpu_hwinfo.tlbsize = 64;
			bootcpu_hwinfo.type = (cpucomp->cpu_prid >> 8) & 0xff;
		} else
			ncpusfound++;
		break;

	case KLSTRUCT_MEMBNK:
		memcomp_m = (klmembnk_m_t *)comp;
#ifdef DEBUG
		memcomp_n = (klmembnk_n_t *)comp;
		DB_PRF(("\t  %dMB, select %x flags %x\n",
		    memcomp_m->membnk_memsz, memcomp_m->membnk_dimm_select,
		    kl_n_mode ?
		      memcomp_n->membnk_attr : memcomp_m->membnk_attr));

		if (kl_n_mode) {
			for (i = 0; i < MD_MEM_BANKS_N; i++) {
				if (memcomp_n->membnk_bnksz[i] == 0)
					continue;
				DB_PRF(("\t\tbank %d %dMB\n",
				    i + 1, memcomp_n->membnk_bnksz[i]));
			}
		} else {
			for (i = 0; i < MD_MEM_BANKS_M; i++) {
				if (memcomp_m->membnk_bnksz[i] == 0)
					continue;
				DB_PRF(("\t\tbank %d %dMB\n",
				    i + 1, memcomp_m->membnk_bnksz[i]));
			}
		}
#endif
		if (ip35)
			kl_add_memory_ip35(comp->nasid,
			    memcomp_m->membnk_bnksz,
			    kl_n_mode ?  MD_MEM_BANKS_N : MD_MEM_BANKS_M);
		else
			kl_add_memory_ip27(comp->nasid,
			    memcomp_m->membnk_bnksz,
			    kl_n_mode ?  MD_MEM_BANKS_N : MD_MEM_BANKS_M);
		break;

	case KLSTRUCT_GFX:
		/*
		 * We rely upon the PROM setting up a fake ARCBios component
		 * for the graphics console, if there is one.
		 * Of course, the ARCBios structure is only available as long
		 * as we do not tear down the PROM TLB, which is why we check
		 * for this as early as possible and remember the console
		 * component (KL struct are not short-lived).
		 */
		if (arc != NULL &&
		    arc->class != 0 && arc->type == arc_DisplayController &&
		    ISSET(arc->flags, ARCBIOS_DEVFLAGS_CONSOLE_OUTPUT)) {
			DB_PRF(("\t  (console device)\n"));
			/* paranoia */
			if (comp->widid >= WIDGET_MIN &&
			    comp->widid <= WIDGET_MAX)
				kl_glass_console = comp;
		}
		break;

	case KLSTRUCT_XBOW:
		xbowcomp = (klxbow_t *)comp;
		DB_PRF(("\t hub master link %d\n",
		    xbowcomp->xbow_hub_master_link));
		kl_hub_widget[comp->nasid] = xbowcomp->xbow_hub_master_link;
#ifdef DEBUG
		for (i = 0; i < MAX_XBOW_LINKS; i++) {
			if (!ISSET(xbowcomp->xbow_port_info[i].port_flag,
			    XBOW_PORT_ENABLE))
				continue;
			DB_PRF(("\t\twidget %d nasid %d flg %u\n", 8 + i,
			    xbowcomp->xbow_port_info[i].port_nasid,
			    xbowcomp->xbow_port_info[i].port_flag));
		}
#endif
		break;

#ifdef DEBUG
	case KLSTRUCT_HUB:
		hubcomp = (klhub_t *)comp;
		DB_PRF(("\t  port %d flag %d speed %dMHz\n",
		    hubcomp->hub_port.port_nasid, hubcomp->hub_port.port_flag,
		    hubcomp->hub_speed / 1000000));
		break;

	case KLSTRUCT_SCSI2:
		scsi2comp = (klscctl_t *)comp;
		for (i = 0; i < scsi2comp->scsi_buscnt; i++) {
			scsicomp = (klscsi_t *)scsi2comp->scsi_bus[i];
			if (scsicomp == NULL)
				continue;
			DB_PRF(("\t\tbus %d, physid 0x%02x virtid %d,"
			    " specific %ld, numdevs %d\n",
			    i, scsicomp->scsi_info.physid,
			    scsicomp->scsi_info.virtid,
			    scsicomp->scsi_specific,
			    scsicomp->scsi_numdevs));
		}
		break;
#endif
	}

#ifdef DEBUG
	if (arc != NULL) {
		DB_PRF(("\t[ARCBios component:"
		    " class %d type %d flags %02x key 0x%lx",
		    arc->class, arc->type, arc->flags, arc->key));
		if (arc->id_len != 0)
			DB_PRF((" %.*s]\n",
			    (int)arc->id_len, (const char *)arc->id));
		else
			DB_PRF((" (no name)]\n"));
	}
#endif

	return 0;
}

/*
 * Enumerate the boards of all nodes, and invoke a callback for those
 * matching the given class.
 */
int
kl_scan_all_nodes(uint cls, int (*cb)(lboard_t *, void *), void *cbarg)
{
	uint node;

	if (kl_scan_node(masternasid, cls, cb, cbarg) != 0)
		return 1;
	for (node = 0; node < maxnodes; node++) {
		if (gda->nasid[node] < 0)
			continue;
		if (gda->nasid[node] == masternasid)
			continue;
		if (kl_scan_node(gda->nasid[node], cls, cb, cbarg) != 0)
			return 1;
	}

	return 0;
}

/*
 * Enumerate the boards of a node, and invoke a callback for those matching
 * the given class.
 */
int
kl_scan_node(int nasid, uint clss, int (*cb)(lboard_t *, void *), void *cbarg)
{
	lboard_t *boardinfo;

	for (boardinfo = IP27_KLFIRST_BOARD(nasid); boardinfo != NULL;
	    boardinfo = IP27_KLNEXT_BOARD(nasid, boardinfo)) {
		if (clss == KLBRD_ANY ||
		    (boardinfo->brd_type & IP27_BC_MASK) == clss) {
			if ((*cb)(boardinfo, cbarg) != 0)
				return 1;
		}
		if (boardinfo->brd_next == 0)
			break;
	}

	return 0;
}

/*
 * Enumerate the components of a board, and invoke a callback for those
 * matching the given type.
 */
int
kl_scan_board(lboard_t *boardinfo, uint type, int (*cb)(klinfo_t *, void *),
    void *cbarg)
{
	klinfo_t *comp;
	int i;

	if (!ISSET(boardinfo->struct_type, LBOARD))
		return 0;

	for (i = 0; i < boardinfo->brd_numcompts; i++) {
		comp = IP27_UNCAC_ADDR(klinfo_t *, boardinfo->brd_nasid,
		    boardinfo->brd_compts[i]);

		if (!ISSET(comp->flags, KLINFO_ENABLED) ||
		    ISSET(comp->flags, KLINFO_FAILED))
			continue;

		if (type != KLSTRUCT_ANY && comp->struct_type != type)
			continue;

		if ((*cb)(comp, cbarg) != 0)
			return 1;
	}

	return 0;
}

/*
 * Return the console device information.
 */
console_t *
kl_get_console()
{
	kl_config_hdr_t *cfghdr = IP27_KLCONFIG_HDR(0);

	return &cfghdr->cons_info;
}

/*
 * Process memory bank information.
 * There are two different routines, because IP27 and IP35 do not
 * layout memory the same way.
 */

/*
 * On IP27, access to each DIMM is interleaved, which cause it to map to
 * four banks on 128MB boundaries.
 * DIMMs of 128MB or smaller map everything in the first bank, though --
 * interleaving would be horribly non-optimal.
 */
void
kl_add_memory_ip27(int16_t nasid, int16_t *sizes, unsigned int cnt)
{
	paddr_t basepa;
	uint64_t fp, lp, np;
	unsigned int seg, nmeg;

	basepa = (paddr_t)nasid << kl_n_shift;
	while (cnt-- != 0) {
		nmeg = *sizes++;
		for (seg = 0; seg < 4; basepa += (1 << 27), seg++) {
			if (nmeg <= 128)
				np = seg == 0 ? nmeg : 0;
			else
				np = nmeg / 4;
			if (np == 0)
				continue;

			DB_PRF(("IP27 memory from %p to %p (%u MB)\n",
			    basepa, basepa + (np << 20), np));

			np = atop(np << 20);	/* MB to pages */
			fp = atop(basepa);
			lp = fp + np;

			/*
			 * We do not manage the first 32MB, so skip them here
			 * if necessary.
			 */
			if (fp < atop(32 << 20)) {
				fp = atop(32 << 20);
				if (fp >= lp)
					continue;
				np = lp - fp;
				physmem += atop(32 << 20);
			}

			if (memrange_register(fp, lp,
			    ~(atop(1UL << kl_n_shift) - 1)) != 0) {
				/*
				 * We could hijack the smallest segment here.
				 * But is it really worth doing?
				 */
				bios_printf("%lu MB of memory could not be "
				    "managed, increase MAXMEMSEGS\n",
				    ptoa(np) >> 20);
			}
		}
	}
}

/*
 * On IP35, the smallest memory DIMMs are 256MB, and the largest is 1GB.
 * Memory is reported at 1GB intervals.
 */
void
kl_add_memory_ip35(int16_t nasid, int16_t *sizes, unsigned int cnt)
{
	paddr_t basepa;
	uint64_t fp, lp, np;

	basepa = (paddr_t)nasid << kl_n_shift;
	while (cnt-- != 0) {
		np = *sizes++;
		if (np != 0) {
			DB_PRF(("IP35 memory from %p to %p (%u MB)\n",
			    basepa, basepa + (np << 20), np));

			fp = atop(basepa);
			np = atop(np << 20);	/* MB to pages */
			lp = fp + np;

			/*
			 * We do not manage the first 64MB, so skip them here
			 * if necessary.
			 */
			if (fp < atop(64 << 20)) {
				fp = atop(64 << 20);
				if (fp >= lp)
					continue;
				np = lp - fp;
				physmem += atop(64 << 20);
			}

			if (memrange_register(fp, lp,
			    ~(atop(1UL << kl_n_shift) - 1)) != 0) {
				/*
				 * We could hijack the smallest segment here.
				 * But is it really worth doing?
				 */
				bios_printf("%lu MB of memory could not be "
				    "managed, increase MAXMEMSEGS\n",
				    ptoa(np) >> 20);
			}
		}
		basepa += 1UL << 30;	/* 1 GB */
	}
}

/*
 * Extract unique device location from a klinfo structure.
 */
void
kl_get_location(klinfo_t *cmp, struct sgi_device_location *sdl)
{
	uint32_t wid;
	int device = cmp->physid;

	/*
	 * If the widget is actually a PIC, we need to compensate
	 * for PCI device numbering.
	 */
	if (xbow_widget_id(cmp->nasid, cmp->widid, &wid) == 0) {
		if (WIDGET_ID_VENDOR(wid) == XBOW_VENDOR_SGI3 &&
		    WIDGET_ID_PRODUCT(wid) == XBOW_PRODUCT_SGI3_PIC)
			device--;
	}

	sdl->nasid = cmp->nasid;
	sdl->widget = cmp->widid;
	if (sys_config.system_type == SGI_IP35) {
		/*
		 * IP35: need to be aware of secondary buses on PIC, and
		 * multifunction PCI cards.
		 */
		sdl->bus = cmp->pci_bus_num;
		sdl->device = device;
		if (cmp->pci_multifunc)
			sdl->fn = cmp->pci_func_num;
		else
			sdl->fn = -1;
		sdl->specific = cmp->port;
	} else {
		/*
		 * IP27: secondary buses and multifunction PCI devices are
		 * not recognized.
		 */
		sdl->bus = 0;
		sdl->device = device;
		sdl->fn = -1;
		sdl->specific = 0;
	}
}

/*
 * Similar to the above, but for the input console device, which information
 * does not come from a klinfo structure.
 */
void
kl_get_console_location(console_t *cons, struct sgi_device_location *sdl)
{
	uint32_t wid;
	int device = cons->npci;

	/*
	 * If the widget is actually a PIC, we need to compensate
	 * for PCI device numbering.
	 */
	if (xbow_widget_id(cons->nasid, cons->wid, &wid) == 0) {
		if (WIDGET_ID_VENDOR(wid) == XBOW_VENDOR_SGI3 &&
		    WIDGET_ID_PRODUCT(wid) == XBOW_PRODUCT_SGI3_PIC)
			device--;
	}

	sdl->nasid = cons->nasid;
	sdl->widget = cons->wid;
	sdl->fn = -1;
	sdl->specific = cons->type;

	/*
	 * This is a disgusting hack. The console structure does not
	 * contain a precise PCI device identification, and will also
	 * not point to the relevant klinfo structure.
	 *
	 * We assume that if the console `type' is not the IOC3 PCI
	 * identifier, then it is an IOC4 device connected to a PIC
	 * bus, therefore we can compute proper PCI location from the
	 * `npci' field.
	 */

	if (cons->type == PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3) ||
	    sys_config.system_type != SGI_IP35) {
		sdl->bus = 0;
		sdl->device = device;
	} else {
		sdl->bus = cons->npci & KLINFO_PHYSID_PIC_BUS1 ? 1 : 0;
		sdl->device = device & KLINFO_PHYSID_WIDGET_MASK;
	}
}


code:
/*	$OpenBSD: wscons_machdep.c,v 1.11 2012/04/18 17:28:24 miod Exp $ */

/*
 * Copyright (c) 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2001 Aaron Campbell
 * All rights reserved.
 *
 * 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 OR HIS RELATIVES 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 MIND, 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/kernel.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/extent.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#if defined(TGT_ORIGIN)
#include <machine/mnode.h>
#endif

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

#include <dev/cons.h>
#include <dev/ic/i8042reg.h>
#include <dev/ic/pckbcvar.h>
#include <dev/usb/ukbdvar.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>

#include <sgi/dev/gbereg.h>
#include <sgi/dev/impactvar.h>
#include <sgi/dev/iockbcvar.h>
#include <sgi/dev/mkbcreg.h>
#include <sgi/gio/giovar.h>
#include <sgi/hpc/hpcreg.h>
#include <sgi/hpc/hpcvar.h>
#include <sgi/hpc/iocreg.h>
#include <sgi/localbus/crimebus.h>
#include <sgi/localbus/macebus.h>
#include <sgi/localbus/macebusvar.h>
#include <sgi/xbow/odysseyvar.h>

#if defined(TGT_OCTANE)
#include <sgi/sgi/ip30.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#endif

#include "gbe.h"
#include "gio.h"
#include "iockbc.h"
#include "impact.h"
#include "mkbc.h"
#include "odyssey.h"
#include "pckbc.h"
#include "ukbd.h"
#include "zskbd.h"

cons_decl(ws);
extern bus_addr_t comconsaddr;

#if defined(TGT_OCTANE) || defined(TGT_ORIGIN)
struct	sgi_device_location	console_output;
struct	sgi_device_location	console_input;
int	(*output_widget_cninit)(void) = NULL;

int	widget_cnprobe(void);
void	widget_cnattach(void);
#endif

void
wscnprobe(struct consdev *cp)
{
	int maj;

	/* Locate the major number. */
	for (maj = 0; maj < nchrdev; maj++) {
		if (cdevsw[maj].d_open == wsdisplayopen)
			break;
	}

	if (maj == nchrdev) {
		/* We are not in cdevsw[], give up. */
		panic("wsdisplay is not in cdevsw[]");
	}

	cp->cn_dev = makedev(maj, 0);
	cp->cn_pri = CN_DEAD;

        switch (sys_config.system_type) {
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
	case SGI_IP20:
	case SGI_IP22:
	case SGI_IP26:
	case SGI_IP28:
#if NGIO > 0
		if (giofb_cnprobe() == 0) {
			if (strncmp(bios_console, "video", 5) == 0)
				cp->cn_pri = CN_FORCED;
			else
				cp->cn_pri = CN_MIDPRI;
		}
#endif
		break;
#endif	/* TGT_INDIGO || TGT_INDY || TGT_INDIGO2 */

#if defined(TGT_O2)
	case SGI_O2:
#if NGBE > 0
		if (gbe_cnprobe(&crimebus_tag, GBE_BASE) != 0) {
			if (strncmp(bios_console, "video", 5) == 0)
				cp->cn_pri = CN_FORCED;
			else
				cp->cn_pri = CN_MIDPRI;
		}
#endif
		break;
#endif	/* TGT_O2 */

#if defined(TGT_ORIGIN)
	case SGI_IP27:
	case SGI_IP35:
		if (widget_cnprobe() != 0) {
			if (strncmp(bios_console,
			    "/dev/graphics/textport", 22) == 0)
				cp->cn_pri = CN_FORCED;
			else
				cp->cn_pri = CN_MIDPRI;
		}
		break;
#endif	/* TGT_ORIGIN */

#if defined(TGT_OCTANE)
	case SGI_OCTANE:
		if (widget_cnprobe() != 0) {
			if (strncmp(bios_console, "video", 5) == 0)
				cp->cn_pri = CN_FORCED;
			else
				cp->cn_pri = CN_MIDPRI;
		}
		break;
#endif	/* TGT_OCTANE */

	default:
		break;
	}
}

void
wscninit(struct consdev *cp)
{
static int initted;

	if (initted)
		return;

	initted = 1;

        switch (sys_config.system_type) {
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
	case SGI_IP20:
	case SGI_IP22:
	case SGI_IP26:
	case SGI_IP28:
#if NGIO > 0
		if (giofb_cnattach() != 0)
			return;
#endif
		if (sys_config.system_type == SGI_IP20) {
#if NZSKBD > 0
			extern void zskbd_cnattach(int, int);
			zskbd_cnattach(0, 0);
#endif
		} else {
#if NPCKBC > 0
			if (pckbc_cnattach(&hpc3bus_tag,
			    HPC_BASE_ADDRESS_0 + IOC_BASE + IOC_KB_REGS + 3,
			    KBCMDP - KBDATAP, 0))
				return;
#endif
		}
		break;
#endif	/* TGT_INDIGO || TGT_INDY || TGT_INDIGO2 */

#if defined(TGT_O2)
	case SGI_O2:
#if NGBE > 0
		if (gbe_cnattach(&crimebus_tag, GBE_BASE) != 0)
			return;
#endif

#if NMKBC > 0
		if (mkbc_cnattach(&macebus_tag, MACE_IO_KBC_OFFS) == 0)
			return;	/* console keyboard found */
#endif
#if NUKBD > 0
		/* fallback keyboard console attachment if the others failed */
		ukbd_cnattach();
#endif
		break;
#endif	/* TGT_O2 */

#if defined(TGT_OCTANE) || defined(TGT_ORIGIN)
	case SGI_IP27:
	case SGI_IP35:
	case SGI_OCTANE:
		widget_cnattach();
		break;
#endif	/* TGT_OCTANE || TGT_ORIGIN */

	default:
		break;
	}
}

void
wscnputc(dev_t dev, int i)
{
	wsdisplay_cnputc(dev, i);
}

int
wscngetc(dev_t dev)
{
	int c;

	wskbd_cnpollc(dev, 1);
	c = wskbd_cngetc(dev);
	wskbd_cnpollc(dev, 0);

	return c;
}

void
wscnpollc(dev_t dev, int on)
{
	wskbd_cnpollc(dev, on);
}

/*
 * Try to figure out if we have a glass console widget, and if we have
 * a driver for it.
 */

#if defined(TGT_OCTANE) || defined(TGT_ORIGIN)
int
widget_cnprobe()
{
#ifdef TGT_ORIGIN
	console_t *cons;
#endif

        switch (sys_config.system_type) {
#ifdef TGT_ORIGIN
	case SGI_IP27:
	case SGI_IP35:
		/*
		 * Our first pass over the KL configuration data has figured
		 * out which component is the glass console, if any.
		 */
		if (kl_glass_console == NULL)
			return 0;
		kl_get_location(kl_glass_console, &console_output);

		cons = kl_get_console();
		kl_get_console_location(cons, &console_input);
		break;
#endif
#ifdef TGT_OCTANE
	case SGI_OCTANE:
		console_output.nasid = masternasid;
		console_output.widget = ip30_find_video();
		if (console_output.widget == 0)
			return 0;

		console_input.nasid = masternasid;
		console_input.widget = IP30_BRIDGE_WIDGET;
		console_input.bus = 0;
		console_input.device = IP30_IOC_SLOTNO;
		console_input.fn = -1;
		console_input.specific =
		    PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3);
		break;
#endif
	default:
		return 0;
	}

	/*
	 * Try supported frame buffers in no particular order.
	 */

#if NIMPACT_XBOW > 0
	if (impact_xbow_cnprobe() != 0) {
		output_widget_cninit = impact_xbow_cnattach;
		goto success;
	}
#endif
#if NODYSSEY > 0
	if (odyssey_cnprobe() != 0) {
		output_widget_cninit = odyssey_cnattach;
		goto success;
	}
#endif

	return 0;

success:
	/*
	 * At this point, we are commited to setup a glass console,
	 * so prevent serial console from winning over.
	 */
	comconsaddr = 0;

	return 1;
}

void
widget_cnattach()
{
	/* should not happen */
	if (output_widget_cninit == NULL)
		return;

	if ((*output_widget_cninit)() != 0)
		return;

#if NIOCKBC > 0
	if (iockbc_cnattach() == 0)
		return;	/* console keyboard found */
#endif
#if NUKBD > 0
	/* fallback keyboard console attachment if the others failed */
	ukbd_cnattach();
#endif
}
#endif	/* TGT_ORIGIN || TGT_OCTANE */


code: src/sys/arch/sgi/stand

path: sys/arch/sgi/stand/CVS/Repository


code:
# $OpenBSD: Makefile,v 1.8 2012/03/28 20:44:23 miod Exp $

SUBDIR=	sgivol

.if ${MACHINE} == "sgi"
SUBDIR+=	libsa libsa32 libz libz32
SUBDIR+=	boot64 boot32 bootecoff
.endif

.include <bsd.subdir.mk>

path: sys/arch/sgi/stand/Makefile


code:
#	$OpenBSD: Makefile.inc,v 1.10 2012/10/19 13:51:59 miod Exp $
#	$NetBSD: Makefile.inc,v 1.7 2000/08/20 14:57:16 mrg Exp $

.ifndef __INCLUDED_STAND_MAKEFILE_INC
__INCLUDED_STAND_MAKEFILE_INC=

BINDIR=		/usr/mdec

STANDALONE?=	-D_STANDALONE

.if ${MACHINE} == "sgi"
CPPFLAGS+=	${STANDALONE}
CPPFLAGS+=	-I.

CFLAGS+=	-fno-stack-protector -Wall
CFLAGS+=	-fno-builtin-vprintf -fno-builtin-printf -fno-builtin-putchar
CFLAGS+=	-fno-builtin-exit
SAABI?=		-mips3 -mno-abicalls -D_NO_ABICALLS -G 0 -fno-pic -fno-common
AS?=		as
LD?=		ld
LIBSA_CPPFLAGS?= -DNEEDS_HEAP_H
.endif

### Figure out what to use for libsa
LIBSADIR?=               ${.CURDIR}/../libsa

.if exists(${LIBSADIR}/${__objdir})
LIBSAOBJDIR=            ${LIBSADIR}/${__objdir}
.else
LIBSAOBJDIR=            ${LIBSADIR}
.endif

LIBSA=                  ${LIBSAOBJDIR}/libsa.a

### Figure out what to use for libz
LIBZDIR?=                ${.CURDIR}/../libz

.if exists(${LIBZDIR}/${__objdir})
LIBZOBJDIR=             ${LIBZDIR}/${__objdir}
.else
LIBZOBJDIR=             ${LIBZDIR}
.endif

LIBZ=                   ${LIBZOBJDIR}/libz.a

.endif


code:
#	$OpenBSD: Makefile32.inc,v 1.5 2012/10/19 13:51:59 miod Exp $

.ifndef __INCLUDED_STAND_MAKEFILE32_INC
__INCLUDED_STAND_MAKEFILE32_INC=

.if ${MACHINE} == "sgi"
# Silence warnings
CFLAGS+=	-fno-builtin-snprintf
CFLAGS+=	-fno-builtin-memcpy
CFLAGS+=	-fno-builtin-memcmp
CFLAGS+=	-fno-builtin-memset
CFLAGS+=	-fno-builtin-strncpy
CFLAGS+=	-fno-builtin-strncmp
# Override toolchain options to force 32 bit binaries
SAABI=		-mno-abicalls -D_NO_ABICALLS -mabi=32 -mips2 -Wa,-G0
AS?=		as
AS+=		-32
LD?=		ld
LD+=		-m elf32btsmip
LIBSA_CPPFLAGS=
.endif

### Figure out what to use for libsa and libz
LIBSADIR=               ${.CURDIR}/../libsa32
LIBZDIR=                ${.CURDIR}/../libz32

.endif


path: sys/arch/sgi/stand/boot/CVS/Entries


code: src/sys/arch/sgi/stand/boot

path: sys/arch/sgi/stand/boot/CVS/Repository


code:
#	$OpenBSD: Makefile,v 1.15 2012/08/31 08:14:08 pascal Exp $

NOMAN=		noman

CFLAGS+=	${SAABI} -mno-abicalls -D_NO_ABICALLS -nostdinc -D__sgi__ \
		-I${.CURDIR}/../include -I${.CURDIR}/../../../.. \
		-I${.CURDIR}/../../../../lib/libsa \
		-I${.OBJDIR}
CFLAGS+=	-D__INTERNAL_LIBSA_CREAD ${STANDALONE} -fno-pie
LDFLAGS+=	-nopie

AFLAGS+=	${SAABI}

S=		${.CURDIR}/../../../..
SRCS=		start.S arcbios.c boot.c conf.c diskio.c filesystem.c \
		netfs.c netio.c strchr.c strstr.c

.PATH:		${S}/lib/libsa
SRCS+=		loadfile.c

.PATH:		${S}/lib/libkern/arch/mips64 ${S}/lib/libkern
SRCS+=		strlcpy.c memcpy.c strlen.c strrchr.c strlcat.c strncmp.c \
		strcmp.S

CLEANFILES+=	machine mips64

.if !make(libdep) && !make(sadep) && !make(salibdir) && !make(obj)
.BEGIN:
	@([ -h machine ] || ln -s ${.CURDIR}/../../include machine)
	@([ -h mips64 ] || ln -s ${.CURDIR}/../../../mips64/include mips64)
.endif

path: sys/arch/sgi/stand/boot/Makefile