diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-11-08 22:44:17 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-11-08 22:44:17 +0000 |
commit | 3c9d47f3b24544d8c15531bc7bec2541c36fbe8f (patch) | |
tree | c6adaa175f6c10986eda76e3af2b578891fecc5d /sys/arch/sgi | |
parent | 283358efb05640208d72b92dd9670ce2b366d715 (diff) |
Add code to send L1 commands and parse command results; use it to add a way
for IP35 systems with IOC3 onboard Ethernet to get their Ethernet address
since it's no longer stored as an owmac(4) device on the IOC3 device itself.
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)) |