diff options
Diffstat (limited to 'sys/arch/sgi')
-rw-r--r-- | sys/arch/sgi/conf/files.sgi | 3 | ||||
-rw-r--r-- | sys/arch/sgi/pci/ioc.c | 50 | ||||
-rw-r--r-- | sys/arch/sgi/sgi/l1.c | 865 | ||||
-rw-r--r-- | sys/arch/sgi/sgi/l1.h | 73 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/hub.h | 5 |
5 files changed, 974 insertions, 22 deletions
diff --git a/sys/arch/sgi/conf/files.sgi b/sys/arch/sgi/conf/files.sgi index a3a2461f72d..3ff414df6ca 100644 --- a/sys/arch/sgi/conf/files.sgi +++ b/sys/arch/sgi/conf/files.sgi @@ -1,4 +1,4 @@ -# $OpenBSD: files.sgi,v 1.38 2009/11/07 18:56:55 miod Exp $ +# $OpenBSD: files.sgi,v 1.39 2009/11/08 22:44:14 miod Exp $ # # maxpartitions must be first item in files.${ARCH} # @@ -16,6 +16,7 @@ file arch/sgi/sgi/disksubr.c disk file arch/sgi/sgi/ip27_machdep.c tgt_origin file arch/sgi/sgi/ip30_machdep.c tgt_octane file arch/sgi/sgi/ip32_machdep.c tgt_o2 +file arch/sgi/sgi/l1.c tgt_origin file arch/sgi/sgi/machdep.c file arch/sgi/sgi/mainbus.c file arch/sgi/sgi/mutex.c diff --git a/sys/arch/sgi/pci/ioc.c b/sys/arch/sgi/pci/ioc.c index 2c40a01b4ea..79559c6b4fb 100644 --- a/sys/arch/sgi/pci/ioc.c +++ b/sys/arch/sgi/pci/ioc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ioc.c,v 1.27 2009/11/08 13:10:03 miod Exp $ */ +/* $OpenBSD: ioc.c,v 1.28 2009/11/08 22:44:16 miod Exp $ */ /* * Copyright (c) 2008 Joel Sing. @@ -31,7 +31,10 @@ #include <machine/autoconf.h> #include <machine/bus.h> +#ifdef TGT_ORIGIN #include <sgi/sgi/ip27.h> +#include <sgi/sgi/l1.h> +#endif #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -106,6 +109,7 @@ int iocow_read_byte(void *); int iocow_triplet(void *, int); int iocow_pulse(struct ioc_softc *, int, int); +#ifdef TGT_ORIGIN /* * A mask of nodes on which an ioc driver has attached. * We use this to prevent attaching a pci IOC3 card which NIC has failed, @@ -114,6 +118,7 @@ int iocow_pulse(struct ioc_softc *, int, int); * XXX This obviously will not work in N mode... */ static uint64_t ioc_nodemask = 0; +#endif int ioc_match(struct device *parent, void *match, void *aux) @@ -258,6 +263,7 @@ ioc_attach(struct device *parent, struct device *self, void *aux) goto unknown; } } else { +#ifdef TGT_ORIGIN /* * If no owserial device has been found, then it is * very likely that we are the on-board IOC3 found @@ -267,7 +273,7 @@ ioc_attach(struct device *parent, struct device *self, void *aux) if ((sys_config.system_type == SGI_IP27 || sys_config.system_type == SGI_IP35) && !ISSET(ioc_nodemask, 1UL << currentnasid)) { - SET(currentnasid, 1UL << currentnasid); + SET(ioc_nodemask, 1UL << currentnasid); device_mask = (1 << IOCDEV_SERIAL_A) | (1 << IOCDEV_SERIAL_B) | (1 << IOCDEV_LPT) | @@ -286,13 +292,9 @@ ioc_attach(struct device *parent, struct device *self, void *aux) rtcbase = IOC3_BYTEBUS_0; dual_irq = 1; - /* - * XXX On IP35 class machines, there are no - * XXX Number-In-a-Can chips to tell us the - * XXX Ethernet address, we need to query - * XXX the L1 controller. - */ - } else { + } else +#endif + { unknown: /* * Well, we don't really know what kind of device @@ -464,6 +466,8 @@ ioc_attach_child(struct device *ioc, const char *name, bus_addr_t base, int dev) struct ioc_softc *sc = (struct ioc_softc *)ioc; struct ioc_attach_args iaa; + memset(&iaa, 0, sizeof iaa); + iaa.iaa_name = name; iaa.iaa_memt = sc->sc_memt; iaa.iaa_memh = sc->sc_memh; @@ -471,17 +475,23 @@ ioc_attach_child(struct device *ioc, const char *name, bus_addr_t base, int dev) iaa.iaa_base = base; iaa.iaa_dev = dev; - if (sc->sc_owmac != NULL) - memcpy(iaa.iaa_enaddr, sc->sc_owmac->sc_enaddr, 6); - else { - /* - * XXX On IP35, there is no Number-In-a-Can attached to - * XXX the onboard IOC3; instead, the Ethernet address - * XXX is stored in the machine eeprom and can be - * XXX queried by sending the appropriate L1 command - * XXX to the L1 UART. This L1 code is not written yet. - */ - memset(iaa.iaa_enaddr, 0xff, 6); + if (dev == IOCDEV_EF) { + if (sc->sc_owmac != NULL) + memcpy(iaa.iaa_enaddr, sc->sc_owmac->sc_enaddr, 6); + else { +#ifdef TGT_ORIGIN + /* + * On IP35 class machines, there are no + * Number-In-a-Can attached to the onboard + * IOC3; instead, the Ethernet address is + * stored in the Brick EEPROM, and can be + * retrieved with an L1 controller query. + */ + if (l1_get_brick_ethernet_address(currentnasid, + iaa.iaa_enaddr) != 0) +#endif + memset(iaa.iaa_enaddr, 0xff, 6); + } } return config_found_sm(ioc, &iaa, ioc_print, ioc_search_mundane); diff --git a/sys/arch/sgi/sgi/l1.c b/sys/arch/sgi/sgi/l1.c new file mode 100644 index 00000000000..9357b08e0e9 --- /dev/null +++ b/sys/arch/sgi/sgi/l1.c @@ -0,0 +1,865 @@ +/* $OpenBSD: l1.c,v 1.1 2009/11/08 22:44:16 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/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 *); + +static inline +size_t ia_skip(u_char *, 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 %02x\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 %02x\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) + data = l1_serial_getc(nasid) ^ 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, &crc, 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 ENOMEM; + + 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 ENOMEM; + + 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 ENOMEM; + + if ((*b & L1_ARG_BINARY) == 0) + return EINVAL; + + datalen = *b & ~L1_ARG_BINARY; + if (*buflen < 1 + datalen) + return EINVAL; + + 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 = (uint32_t)va_arg(ap, int); + 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)) + continue; /* it's not our response */ + + *pktlen = (size_t)rc; + return 0; + } +} + +/* + * Read a board IA information record from EEPROM + */ + +#define EEPROM_CHUNK 0x40 + +int +l1_read_board_ia(int16_t nasid, 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(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_GENERAL), + L1_REQ_EEPROM, 4, + L1_ARG_INT, L1_EEP_LOGIC, L1_ARG_INT, L1_EEP_BOARD, + L1_ARG_INT, 0 /* offset */, L1_ARG_INT, 0 /* size */); + if (pktlen > sizeof pkt) { +#ifdef DIAGNOSTIC + panic("L1 command packet too large (%zu) for buffer", 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 %d)\n", pktlen); +#endif + return ENXIO; + } + + /* + * Check the response code. + */ + + data = l1_packet_get_be32(&pkt[1]); + if (data != L1_RESP_OK) { +#ifdef L1_DEBUG + printf("unexpected L1 response code: %08x\n", data); +#endif + return ENXIO; + } + + /* + * 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 ENXIO; + } + + 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 ENXIO; + } + + /* + * 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(L1_TYPE_L1, L1_ADDRESS_LOCAL | L1_TASK_GENERAL), + L1_REQ_EEPROM, 4, + L1_ARG_INT, L1_EEP_LOGIC, L1_ARG_INT, L1_EEP_BOARD, + L1_ARG_INT, iapos, L1_ARG_INT, 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 %d)\n", pktlen); +#endif + rc = ENXIO; + goto fail; + } + + /* + * Check the response code. + */ + + data = l1_packet_get_be32(&pkt[1]); + if (data != L1_RESP_OK) { +#ifdef L1_DEBUG + printf("unexpected L1 response code: %08x\n", data); +#endif + rc = ENXIO; + goto fail; + } + + if (pkt[5] != 2) { +#ifdef L1_DEBUG + printf("unexpected L1 response: %d values\n", pkt[5]); +#endif + rc = ENXIO; + 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 = ENXIO; + 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 = ENXIO; + 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 = ENXIO; + goto fail; + } + + memcpy(ia + iapos, chunk, chunklen); + iapos += chunklen; +#ifdef L1_DEBUG + printf("got %02x bytes of eeprom, %x/%x\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); + 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 reviison + * 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) +{ + ia += iapos; + + /* don't go past EOF marker */ + if (*ia == IA_EOF) + return iapos; + + iapos += 1 + (*ia & IA_LENGTH_MASK); + return iapos; +} + +int +l1_get_brick_ethernet_address(int16_t nasid, uint8_t *enaddr) +{ + u_char *ia, *mac; + size_t iapos, ialen; + char hexaddr[18]; + int rc; + + /* read the Board IA of this node */ + rc = l1_read_board_ia(nasid, &ia, &ialen); + if (rc != 0) + return rc; + + /* simple sanity checks */ + if (ia[0] != 0 || ia[1] * 8 > ialen) { + rc = EINVAL; + goto out; + } + + /* skip fixed part */ + iapos = 6; + /* skip 4 records */ + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + /* skip FRU */ + iapos++; + /* skip 3 records */ + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + /* skip encryption key records if applicable */ + if (ia[iapos] == IA_TL(IA_TYPE_BINARY, 4)) { + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + iapos = ia_skip(ia, iapos); + } + /* check the next record looks like an Ethernet address */ + if (ia[iapos] != IA_TL(IA_TYPE_ASCII, 12)) { + rc = EINVAL; + goto out; + } + + iapos++; + mac = ia + iapos; + snprintf(hexaddr, sizeof hexaddr, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + mac[6], mac[7], mac[8], mac[9], mac[10], mac[11]); + enaddr_aton(hexaddr, enaddr); + +out: + free(ia, M_DEVBUF); + return rc; +} diff --git a/sys/arch/sgi/sgi/l1.h b/sys/arch/sgi/sgi/l1.h new file mode 100644 index 00000000000..db400ed7370 --- /dev/null +++ b/sys/arch/sgi/sgi/l1.h @@ -0,0 +1,73 @@ +/* $OpenBSD: l1.h,v 1.1 2009/11/08 22:44:16 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 0x00000000 + +/* + * 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 */ +#define L1_EEP_LOGIC 0x01 /* component: logic board */ +#define L1_EEP_BOARD 0x02 /* ia code: board */ + +int l1_read_board_ia(int16_t, u_char **, size_t *); +int l1_get_brick_ethernet_address(int16_t, uint8_t *); diff --git a/sys/arch/sgi/xbow/hub.h b/sys/arch/sgi/xbow/hub.h index 63dfed9fb54..e0dcc2d75a0 100644 --- a/sys/arch/sgi/xbow/hub.h +++ b/sys/arch/sgi/xbow/hub.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hub.h,v 1.6 2009/10/22 22:08:54 miod Exp $ */ +/* $OpenBSD: hub.h,v 1.7 2009/11/08 22:44:16 miod Exp $ */ /* * Copyright (c) 2009 Miodrag Vallat. @@ -65,9 +65,12 @@ #define LBOOTBASE_IP35 0x18000000 #define LBOOTSIZE_IP35 0x08000000 +#define HSPEC_L1_UARTBASE 0x00000080 #define HSPEC_SYNERGY0 0x04000000 /* synergy #0 base */ #define HSPEC_SYNERGY1 0x05000000 /* synergy #1 base */ +#define HSPEC_L1_UART(r) \ + (LREGBASE_IP35 + HSPEC_L1_UARTBASE + ((r) << 3)) #define HSPEC_SYNERGY(s,r) \ (LREGBASE_IP35 + ((s) ? HSPEC_SYNERGY1 : HSPEC_SYNERGY0) + ((r) << 3)) |