Subject: Re: netboot via native bootloader
To: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
From: Erik Berls <cyber@ono-sendai.com>
List: port-cobalt
Date: 10/27/2007 11:47:02
Izumi,
Excellent work!
I'd like to get this submitted as a pullup request to the netbsd-4
branch, but I'm 3000 miles from home. I'll submit the request when I
get back in a week if no-one beats me to it. [;-)
They probably won't accept it for 4.0, but it should be in 4.1.
-=erik.
On 10/27/07, Izumi Tsutsui <tsutsui@ceres.dti.ne.jp> wrote:
> I wrote:
>
> > Today I've been trying to add netboot support to the cobalt
> > native bootloader, using a standalone tulip (2114x Ethernet)
> > driver for sandpoint which has been committed by Tohru Nishimura:
> :
> > Current my working sources are too ugly so I'll post
> > a patch after some more cleanup in a few days.
>
> A patch to be committed is attached.
>
> I've also put a bootloader binary here:
> http://www.ceres.dti.ne.jp/~tsutsui/netbsd/boot-cobalt-20071028.gz
>
> To test netboot, refer this FAQ entry
> http://www.netbsd.org/ports/cobalt/faq.html#netboot
> and specify bootloader's name (boot.gz or so) instead of kernel.
>
> Note if bootloader is loaded from wd disk it can't load a kernel
> from tlp0, so some more initialization might be required.
> ---
> Izumi Tsutsui
>
>
> Index: stand/boot/Makefile
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/Makefile,v
> retrieving revision 1.11
> diff -u -r1.11 Makefile
> --- stand/boot/Makefile 12 Aug 2006 11:38:11 -0000 1.11
> +++ stand/boot/Makefile 27 Oct 2007 15:50:03 -0000
> @@ -8,8 +8,10 @@
> S= ${.CURDIR}/../../../..
> MIPS= ${S}/arch/mips
> COBALT= ${S}/arch/cobalt
> +LIBSADIR= ${S}/lib/libsa
>
> # .PATH: ${.CURDIR}/../common
> +.PATH: ${LIBSADIR}
>
> BINMODE?= 444
>
> @@ -55,6 +57,9 @@
> CPPFLAGS+= -I${.OBJDIR} -I${S} -I${S}/lib/libsa
> CPPFLAGS+= -DCONS_SERIAL -DCOMBASE=${COMBASE} -DCOMPORT=${COMPORT}
> CPPFLAGS+= -DCOMSPEED=${COMSPEED} -DCOMPROBE=${COMPROBE}
> +CPPFLAGS+= -DSUPPORT_DHCP -DSUPPORT_BOOTP
> +#CPPFLAGS+= -DBOOTP_DEBUG -DNETIF_DEBUG -DETHER_DEBUG -DNFS_DEBUG
> +#CPPFLAGS+= -DRPC_DEBUG -DRARP_DEBUG -DNET_DEBUG -DDEBUG -DPARANOID
>
> # compiler flags for smallest code size
> CFLAGS= -Os -mmemcpy -ffreestanding -mno-abicalls -msoft-float -G 128
> @@ -68,6 +73,10 @@
> # common sources
> SRCS+= start.S boot.c devopen.c conf.c clock.c bootinfo.c
> SRCS+= prf.c com.c cons.c ns16550.c pciide.c tgets.c wdc.c wd.c
> +SRCS+= cache.c if_tlp.c tlp.c
> +
> +# ${S}/lib/libsa
> +SRCS+= dev_net.c
>
> SRCS+= vers.c
> CLEANFILES+= vers.c
> Index: stand/boot/boot.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.c,v
> retrieving revision 1.9
> diff -u -r1.9 boot.c
> --- stand/boot/boot.c 17 Oct 2007 19:54:09 -0000 1.9
> +++ stand/boot/boot.c 27 Oct 2007 15:50:03 -0000
> @@ -75,6 +75,7 @@
>
> #include <lib/libsa/stand.h>
> #include <lib/libsa/loadfile.h>
> +#include <lib/libsa/dev_net.h>
> #include <lib/libkern/libkern.h>
>
> #include <sys/param.h>
> @@ -171,10 +172,27 @@
> DPRINTF(("patch_bootstring: [%s]\n", bootstring));
>
> #define DEVNAMESIZE (MAXDEVNAME + sizeof(" root=/dev/hd") + sizeof("0a"))
> - /* bsd notation -> linux notation (wd0a -> hda1) */
> - if (strlen(bootstring) <= (511 - DEVNAMESIZE)) {
> + if (strcmp(devsw[dev].dv_name, "wd") == 0 &&
> + strlen(bootstring) <= (511 - DEVNAMESIZE)) {
> int len;
>
> + /* omit "nfsroot=" arg on wd boot */
> + if ((sp = strstr(bootstring, "nfsroot=")) != NULL) {
> + const char *end;
> +
> + end = strchr(sp, ' ');
> +
> + /* strip off leading spaces */
> + for (--sp; (sp > bootstring) && (*sp == ' '); --sp)
> + ;
> +
> + if (end != NULL)
> + strcpy(++sp, end);
> + else
> + *++sp = '\0';
> + }
> +
> + /* bsd notation -> linux notation (wd0a -> hda1) */
> strcat(bootstring, " root=/dev/hd");
>
> len = strlen(bootstring);
> @@ -200,12 +218,14 @@
> char *ptr, *spec;
> char c, namebuf[PATH_MAX];
> static char bootdev[] = "wd0a";
> + static const char nfsbootdev[] = "nfs";
>
> bootstr_dev = prompt_dev = NULL;
> bootstr_kname = prompt_kname = NULL;
>
> /* first, get root device specified by the firmware */
> spec = bootstring;
> +
> /* assume the last one is valid */
> while ((spec = strstr(spec, "root=")) != NULL) {
> spec += 5; /* skip 'root=' */
> @@ -239,6 +259,11 @@
> }
> }
>
> + /* third, check if netboot */
> + if (strstr(bootstring, "nfsroot=") != NULL) {
> + bootstr_dev = nfsbootdev;
> + }
> +
> DPRINTF(("bootstr_dev = %s, bootstr_kname = %s\n",
> bootstr_dev ? bootstr_dev : "<NULL>",
> bootstr_kname ? bootstr_kname : "<NULL>"));
> @@ -365,9 +390,10 @@
> struct btinfo_symtab bi_syms;
> struct btinfo_bootpath bi_bpath;
> struct btinfo_howto bi_howto;
> -
> int addr, speed, howto;
>
> + try_bootp = 1;
> +
> /* Initialize boot info early */
> howto = 0x0;
> bi_flags.bi_flags = 0x0;
> Index: stand/boot/boot.h
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/boot.h,v
> retrieving revision 1.5
> diff -u -r1.5 boot.h
> --- stand/boot/boot.h 17 Oct 2007 19:54:09 -0000 1.5
> +++ stand/boot/boot.h 27 Oct 2007 15:50:03 -0000
> @@ -71,8 +71,27 @@
> int wdclose(struct open_file *);
>
> /*
> + * tlp
> + */
> +void *tlp_init(void *);
> +int tlp_send(void *, char *, u_int);
> +int tlp_recv(void *, char *, u_int, u_int);
> +
> +extern struct netif_driver ether_tlp_driver;
> +
> +/*
> * devopen
> */
> int devparse(const char *, int *, uint8_t *, uint8_t *, const char **);
>
> +/*
> + * tgetc
> + */
> int tgets(char *);
> +
> +/*
> + * cache
> + */
> +void pdcache_wb(uint32_t, u_int);
> +void pdcache_inv(uint32_t, u_int);
> +void pdcache_wbinv(uint32_t, u_int);
> Index: stand/boot/clock.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/clock.c,v
> retrieving revision 1.1
> diff -u -r1.1 clock.c
> --- stand/boot/clock.c 25 Jun 2003 17:24:22 -0000 1.1
> +++ stand/boot/clock.c 27 Oct 2007 15:50:03 -0000
> @@ -39,10 +39,20 @@
> #include <sys/types.h>
> #include <lib/libsa/stand.h>
>
> +#include <netinet/in.h>
> +#include <netinet/in_systm.h>
> +#include <lib/libsa/net.h>
> +
> +#include <dev/ic/mc146818reg.h>
> +
> #include "boot.h"
>
> #define DELAY_CALIBRATE 1000
>
> +#define MCCLOCK_BASE 0xa0000070
> +#define MCCLOCK_REG 0
> +#define MCCLOCK_DATA 1
> +
> void
> delay(int ms)
> {
> @@ -51,5 +61,20 @@
> */
> volatile register int N = ms * DELAY_CALIBRATE;
> for (; --N;)
> - ;
> + __insn_barrier();
> +}
> +
> +time_t
> +getsecs(void)
> +{
> + u_int sec;
> +
> + *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_SEC;
> + sec = *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA);
> + *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_MIN;
> + sec += *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA) * 60;
> + *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_REG) = MC_HOUR;
> + sec += *(volatile uint8_t *)(MCCLOCK_BASE + MCCLOCK_DATA) * 60 * 60;
> +
> + return (time_t)sec;
> }
> Index: stand/boot/conf.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/conf.c,v
> retrieving revision 1.4
> diff -u -r1.4 conf.c
> --- stand/boot/conf.c 11 Dec 2005 12:17:06 -0000 1.4
> +++ stand/boot/conf.c 27 Oct 2007 15:50:03 -0000
> @@ -52,6 +52,7 @@
> */
> struct devsw devsw[] = {
> { "wd", wdstrategy, wdopen, wdclose, noioctl },
> + { "nfs", net_strategy, net_open, net_close, net_ioctl }
> };
>
> int ndevs = (sizeof(devsw)/sizeof(devsw[0]));
> @@ -61,19 +62,15 @@
> */
> struct fs_ops file_system[] = {
> FS_OPS(ufs),
> -#if 0
> FS_OPS(nfs),
> -#endif
> };
>
> int nfsys = sizeof(file_system) / sizeof(file_system[0]);
>
> -#if 0
> extern struct netif_driver en_driver;
>
> struct netif_driver *netif_drivers[] = {
> - &en_driver,
> + ðer_tlp_driver,
> };
>
> int n_netif_drivers = sizeof(netif_drivers) / sizeof(netif_drivers[0]);
> -#endif
> Index: stand/boot/devopen.c
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/devopen.c,v
> retrieving revision 1.3
> diff -u -r1.3 devopen.c
> --- stand/boot/devopen.c 17 Oct 2007 19:54:09 -0000 1.3
> +++ stand/boot/devopen.c 27 Oct 2007 15:50:03 -0000
> @@ -77,24 +77,31 @@
> /* extract device name */
> for (i = 0; isalpha(fname[i]) && (i < devlen); i++)
> devname[i] = fname[i];
> - devname[i] = 0;
> + devname[i] = '\0';
>
> - if (!isnum(fname[i]))
> - return EUNIT;
> + if (strcmp(devname, "nfs") == 0) {
> + /* no unit number or partition suffix on netboot */
> + u = 0;
> + p = 0;
> + } else {
> + /* parse [disk][unit][part] (ex. wd0a) strings */
> + if (!isnum(fname[i]))
> + return EUNIT;
> +
> + /* device number */
> + for (u = 0; isnum(fname[i]) && (i < devlen); i++)
> + u = u * 10 + (fname[i] - '0');
> +
> + if (!isalpha(fname[i]))
> + return EPART;
> +
> + /* partition number */
> + if (i < devlen)
> + p = fname[i++] - 'a';
>
> - /* device number */
> - for (u = 0; isnum(fname[i]) && (i < devlen); i++)
> - u = u * 10 + (fname[i] - '0');
> -
> - if (!isalpha(fname[i]))
> - return EPART;
> -
> - /* partition number */
> - if (i < devlen)
> - p = fname[i++] - 'a';
> -
> - if (i != devlen)
> - return ENXIO;
> + if (i != devlen)
> + return ENXIO;
> + }
>
> /* check device name */
> for (dp = devsw, i = 0; i < ndevs; dp++, i++) {
> @@ -132,7 +139,7 @@
>
> dp = &devsw[dev];
> if ((void *)dp->dv_open == (void *)nodev)
> - return ENXIO;
> + return ENXIO;
>
> f->f_dev = dp;
>
> Index: stand/boot/start.S
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/start.S,v
> retrieving revision 1.5
> diff -u -r1.5 start.S
> --- stand/boot/start.S 17 Oct 2007 19:54:09 -0000 1.5
> +++ stand/boot/start.S 27 Oct 2007 15:50:03 -0000
> @@ -64,7 +64,7 @@
> jal _C_LABEL(main) # main(unsigned int)
> move a1, s1 # restore argv
>
> -XLEAF(_xtt)
> +XLEAF(_rtt)
> jal _C_LABEL(cpu_reboot) # failed, reboot
> nop
> END(start)
> Index: stand/boot/version
> ===================================================================
> RCS file: /cvsroot/src/sys/arch/cobalt/stand/boot/version,v
> retrieving revision 1.5
> diff -u -r1.5 version
> --- stand/boot/version 17 Oct 2007 19:54:09 -0000 1.5
> +++ stand/boot/version 27 Oct 2007 15:50:03 -0000
> @@ -9,3 +9,4 @@
> 0.3: Parse boot_flags and pass boothowto value via bootinfo
> 0.4: parse "root=/dev/hdXN" args passed from the firmware and
> set default boot device accordingly
> +0.5: Add support for netboot via tlp0
> --- /dev/null 2007-10-28 00:31:10.000000000 +0900
> +++ stand/boot/cache.c 2007-10-28 00:49:18.000000000 +0900
> @@ -0,0 +1,92 @@
> +/* $NetBSD$ */
> +
> +/*
> + * Copyright 2001 Wasabi Systems, Inc.
> + * All rights reserved.
> + *
> + * Written by Jason R. Thorpe for Wasabi Systems, Inc.
> + *
> + * 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.
> + * 3. All advertising materials mentioning features or use of this software
> + * must display the following acknowledgement:
> + * This product includes software developed for the NetBSD Project by
> + * Wasabi Systems, Inc.
> + * 4. The name of Wasabi Systems, Inc. may not be used to endorse
> + * or promote products derived from this software without specific prior
> + * written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
> + * 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 <lib/libsa/stand.h>
> +#include <lib/libkern/libkern.h>
> +
> +#include <mips/cpuregs.h>
> +#include <mips/cache_r4k.h>
> +
> +#include "boot.h"
> +
> +#define CACHELINESIZE 16
> +#define round_line(x) (((x) + (CACHELINESIZE - 1)) & ~(CACHELINESIZE - 1))
> +#define trunc_line(x) ((x) & ~(CACHELINESIZE - 1))
> +
> +__asm(".set mips3");
> +
> +void
> +pdcache_inv(uint32_t va, u_int size)
> +{
> + uint32_t eva;
> +
> + va = trunc_line(va);
> + eva = round_line(va + size);
> +
> + while (va < eva) {
> + cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_INV);
> + va += CACHELINESIZE;
> + }
> +}
> +
> +void
> +pdcache_wb(uint32_t va, u_int size)
> +{
> + uint32_t eva;
> +
> + va = trunc_line(va);
> + eva = round_line(va + size);
> +
> + while (va < eva) {
> + cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_WB);
> + va += CACHELINESIZE;
> + }
> +}
> +
> +void
> +pdcache_wbinv(uint32_t va, u_int size)
> +{
> + uint32_t eva;
> +
> + va = trunc_line(va);
> + eva = round_line(va + size);
> +
> + while (va < eva) {
> + cache_op_r4k_line(va, CACHE_R4K_D|CACHEOP_R4K_HIT_WB_INV);
> + va += CACHELINESIZE;
> + }
> +}
> --- /dev/null 2007-10-28 00:31:10.000000000 +0900
> +++ stand/boot/if_tlp.c 2007-10-27 15:25:18.000000000 +0900
> @@ -0,0 +1,153 @@
> +/* $NetBSD$ */
> +
> +/*-
> + * Copyright (c) 2004 The NetBSD Foundation, Inc.
> + * All rights reserved.
> + *
> + * This code is derived from software contributed to The NetBSD Foundation
> + * by UCHIYAMA Yashusi.
> + *
> + * 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.
> + * 3. All advertising materials mentioning features or use of this software
> + * must display the following acknowledgement:
> + * This product includes software developed by the NetBSD
> + * Foundation, Inc. and its contributors.
> + * 4. Neither the name of The NetBSD Foundation nor the names of its
> + * contributors may be used to endorse or promote products derived
> + * from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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/socket.h>
> +
> +#include <lib/libsa/stand.h>
> +#include <lib/libkern/libkern.h>
> +
> +#include <sys/socket.h>
> +#include <net/if.h>
> +#include <netinet/in.h>
> +#include <netinet/in_systm.h>
> +
> +#include <lib/libsa/net.h>
> +#include <lib/libsa/netif.h>
> +#include <lib/libsa/dev_net.h>
> +
> +#include "boot.h"
> +
> +static int tlp_match(struct netif *, void *);
> +static int tlp_probe(struct netif *, void *);
> +static void tlp_attach(struct iodesc *, void *);
> +static int tlp_get(struct iodesc *, void *, size_t, time_t);
> +static int tlp_put(struct iodesc *, void *, size_t);
> +static void tlp_end(struct netif *);
> +
> +#define MIN_LEN 60 /* ETHER_MIN_LEN - ETHER_CRC_LEN */
> +
> +static struct netif_stats tlp_stats[1];
> +
> +static struct netif_dif tlp_ifs[] = {
> + { 0, 1, &tlp_stats[0], NULL, 0 },
> +};
> +
> +struct netif_driver ether_tlp_driver = {
> + "tlp",
> + tlp_match,
> + tlp_probe,
> + tlp_attach,
> + tlp_get,
> + tlp_put,
> + tlp_end,
> + tlp_ifs,
> + 1,
> +};
> +
> +#ifdef DEBUG
> +int debug = 1; /* referred in various libsa net sources */
> +#endif
> +
> +int
> +tlp_match(struct netif *netif, void *hint)
> +{
> +
> + /* always match for onboard tlp */
> + return 1;
> +}
> +
> +int
> +tlp_probe(struct netif *netif, void *hint)
> +{
> +
> + /* XXX */
> + return 0;
> +}
> +
> +void
> +tlp_attach(struct iodesc *desc, void *hint)
> +{
> + struct netif *nif = desc->io_netif;
> + struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
> +
> + dif->dif_private = tlp_init(&desc->myea);
> +}
> +
> +int
> +tlp_get(struct iodesc *desc, void *pkt, size_t maxlen, time_t timeout)
> +{
> + int len;
> + struct netif *nif = desc->io_netif;
> + struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
> + struct local *l = dif->dif_private;
> +
> + len = tlp_recv(l, pkt, maxlen, timeout);
> + if (len == -1) {
> + printf("tlp: receive timeout\n");
> + /* XXX */
> + }
> +
> + if (len < MIN_LEN)
> + len = -1;
> +
> + return len;
> +}
> +
> +int
> +tlp_put(struct iodesc *desc, void *pkt, size_t len)
> +{
> + struct netif *nif = desc->io_netif;
> + struct netif_dif *dif = &nif->nif_driver->netif_ifs[nif->nif_unit];
> + struct local *l = dif->dif_private;
> + int rv;
> + size_t sendlen;
> +
> + sendlen = len;
> + if (sendlen < MIN_LEN)
> + sendlen = MIN_LEN; /* XXX */
> +
> + rv = tlp_send(l, pkt, sendlen);
> +
> + return rv;
> +}
> +
> +void
> +tlp_end(struct netif *netif)
> +{
> +}
> --- /dev/null 2007-10-28 00:31:10.000000000 +0900
> +++ stand/boot/tlp.c 2007-10-27 23:04:34.000000000 +0900
> @@ -0,0 +1,463 @@
> +/* $NetBSD$ */
> +
> +/*-
> + * Copyright (c) 2007 The NetBSD Foundation, Inc.
> + * All rights reserved.
> + *
> + * This code is derived from software contributed to The NetBSD Foundation
> + * by Tohru Nishimura.
> + *
> + * 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.
> + * 3. All advertising materials mentioning features or use of this software
> + * must display the following acknowledgement:
> + * This product includes software developed by the NetBSD
> + * Foundation, Inc. and its contributors.
> + * 4. Neither the name of The NetBSD Foundation nor the names of its
> + * contributors may be used to endorse or promote products derived
> + * from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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/socket.h>
> +
> +#include <netinet/in.h>
> +#include <netinet/in_systm.h>
> +
> +#include <lib/libsa/stand.h>
> +#include <lib/libsa/net.h>
> +
> +#include <mips/cpuregs.h>
> +
> +#include "boot.h"
> +
> +/*
> + * - little endian access for CSR register.
> + * - assume KSEG0 for vtophys() translation.
> + * - PIPT writeback cache aware.
> + */
> +#define CSR_WRITE(l, r, v) \
> +do { \
> + *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1((l)->csr + (r)) = (v); \
> +} while (0)
> +#define CSR_READ(l, r) \
> + *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1((l)->csr + (r))
> +#define VTOPHYS(va) MIPS_KSEG0_TO_PHYS(va)
> +#define wb(adr, siz) pdcache_wb((uint32_t)(adr), (uint32_t)(siz))
> +#define wbinv(adr, siz) pdcache_wbinv((uint32_t)(adr), (uint32_t)(siz))
> +#define inv(adr, siz) pdcache_inv((uint32_t)(adr), (uint32_t)(siz))
> +#define DELAY(n) delay(n)
> +#define ALLOC(T, A) (T *)((uint32_t)alloc(sizeof(T) + (A)) & ~((A) - 1))
> +
> +#define T0_OWN (1U<<31) /* desc is ready to tx */
> +#define T0_ES (1U<<15) /* Tx error summary */
> +#define T1_LS (1U<<30) /* last segment */
> +#define T1_FS (1U<<29) /* first segment */
> +#define T1_SET (1U<<27) /* "setup packet" */
> +#define T1_TER (1U<<25) /* end of ring mark */
> +#define T1_TBS_MASK 0x7ff /* segment size 10:0 */
> +#define R0_OWN (1U<<31) /* desc is empty */
> +#define R0_FS (1U<<30) /* first desc of frame */
> +#define R0_LS (1U<<8) /* last desc of frame */
> +#define R0_ES (1U<<15) /* Rx error summary */
> +#define R1_RER (1U<<25) /* end of ring mark */
> +#define R0_FL_MASK 0x3fff0000 /* frame length 29:16 */
> +#define R1_RBS_MASK 0x7ff /* segment size 10:0 */
> +
> +struct desc {
> + volatile uint32_t xd0, xd1, xd2, xd3;
> +};
> +
> +
> +#define TLP_BMR 0x000 /* 0: bus mode */
> +#define BMR_RST (1U<< 0) /* software reset */
> +#define TLP_TPD 0x008 /* 1: instruct Tx to start */
> +#define TPD_POLL (1U<< 0) /* transmit poll demand */
> +#define TLP_RPD 0x010 /* 2: instruct Rx to start */
> +#define RPD_POLL (1U<< 0) /* receive poll demand */
> +#define TLP_RRBA 0x018 /* 3: Rx descriptor base */
> +#define TLP_TRBA 0x020 /* 4: Tx descriptor base */
> +#define TLP_STS 0x028 /* 5: status */
> +#define STS_TS 0x00700000 /* Tx status */
> +#define STS_RS 0x000e0000 /* Rx status */
> +#define TLP_OMR 0x030 /* 6: operation mode */
> +#define OMR_SDP (1U<<25) /* always ON */
> +#define OMR_PS (1U<<18) /* port select */
> +#define OMR_PM (1U<< 6) /* promicuous */
> +#define OMR_TEN (1U<<13) /* instruct start/stop Tx */
> +#define OMR_REN (1U<< 1) /* instruct start/stop Rx */
> +#define OMR_FD (1U<< 9) /* FDX */
> +#define TLP_IEN 0x38 /* 7: interrupt enable mask */
> +#define TLP_APROM 0x048 /* 9: SEEPROM and MII management */
> +#define SROM_RD (1U <<14) /* read operation */
> +#define SROM_WR (1U <<13) /* write openration */
> +#define SROM_SR (1U <<11) /* SEEPROM select */
> +#define TLP_CSR12 0x60 /* SIA status */
> +
> +#define TLP_CSR15 0x78 /* SIA general register */
> +#define SIAGEN_MD0 (1U<<16)
> +#define SIAGEN_CWE (1U<<28)
> +
> +
> +#define FRAMESIZE 1536
> +#define BUFSIZE 2048
> +#define NRXBUF 2
> +#define NEXT_RXBUF(x) ((x + 1) & (NRXBUF - 1))
> +
> +struct local {
> + struct desc TxD;
> + struct desc RxD[NRXBUF];
> + uint8_t txstore[BUFSIZE];
> + uint8_t rxstore[NRXBUF][BUFSIZE];
> + uint32_t csr, omr, rx;
> + u_int sromsft;
> + u_int phy, bmsr, anlpar;
> +};
> +
> +#define COBALT_TLP0_BASE 0x10100000
> +#define EEP_MAC_OFFSET 0
> +
> +static void size_srom(struct local *);
> +static int read_srom(struct local *, int);
> +#if 0
> +static u_int tlp_mii_read(struct local *, int, int);
> +static void tlp_mii_write(struct local *, int, int, int);
> +static void mii_initphy(struct local *);
> +#endif
> +
> +void *
> +tlp_init(void *cookie)
> +{
> + uint32_t tag, val;
> + struct local *l;
> + struct desc *TxD, *RxD;
> + uint8_t *en;
> + int i;
> +
> + l = ALLOC(struct local, sizeof(struct desc));
> + memset(l, 0, sizeof(struct local));
> +
> +#if 0
> + /* XXX assume tlp0 at pci0 dev 7 function 0 */
> + tag = (0 << 16) | ( 7 << 11) | (0 << 8);
> + /* memory map is not initialized by the firmware on cobalt */
> + l->csr = pcicfgread(tag, 0x10);
> + l->csr &= 0xfffffffc;
> + DPRINTF(("%s: CSR = 0x%x\n", __func__, l->csr));
> +#else
> +
> + l->csr = COBALT_TLP0_BASE;
> +#endif
> +
> + val = CSR_READ(l, TLP_BMR);
> + CSR_WRITE(l, TLP_BMR, val | BMR_RST);
> + DELAY(1000);
> + CSR_WRITE(l, TLP_BMR, val);
> + DELAY(1000);
> + (void)CSR_READ(l, TLP_BMR);
> +
> + l->omr = OMR_PS | OMR_SDP;
> + CSR_WRITE(l, TLP_OMR, l->omr);
> + CSR_WRITE(l, TLP_STS, ~0);
> + CSR_WRITE(l, TLP_IEN, 0);
> +
> +#if 0
> + mii_initphy(l);
> +#endif
> + size_srom(l);
> +
> + en = cookie;
> + /* MAC address is stored at offset 0 in SROM on cobalt */
> + val = read_srom(l, EEP_MAC_OFFSET / 2 + 0);
> + en[0] = val;
> + en[1] = val >> 8;
> + val = read_srom(l, EEP_MAC_OFFSET / 2 + 1);
> + en[2] = val;
> + en[3] = val >> 8;
> + val = read_srom(l, EEP_MAC_OFFSET / 2 + 2);
> + en[4] = val;
> + en[5] = val >> 8;
> +
> + DPRINTF(("tlp: MAC address %x:%x:%x:%x:%x:%x\n",
> + en[0], en[1], en[2], en[3], en[4], en[5]));
> +
> + RxD = &l->RxD[0];
> + for (i = 0; i < NRXBUF; i++) {
> + RxD[i].xd0 = htole32(R0_OWN);
> + RxD[i].xd1 = htole32(FRAMESIZE);
> + RxD[i].xd2 = htole32(VTOPHYS(l->rxstore[i]));
> + RxD[i].xd3 = htole32(VTOPHYS(&RxD[NEXT_RXBUF(i)]));
> + }
> + RxD[NRXBUF - 1].xd1 |= htole32(R1_RER);
> + CSR_WRITE(l, TLP_RRBA, VTOPHYS(RxD));
> +
> + /* "setup packet" to have own station address */
> + TxD = &l->TxD;
> + TxD->xd3 = htole32(VTOPHYS(TxD));
> + TxD->xd2 = htole32(VTOPHYS(l->txstore));
> + TxD->xd1 = htole32(T1_SET | T1_TER);
> + TxD->xd0 = htole32(0);
> + CSR_WRITE(l, TLP_TRBA, VTOPHYS(TxD));
> +
> + memset(l->txstore, 0, FRAMESIZE);
> +
> + /* make sure the entire descriptors transfered to memory */
> + wbinv(l, sizeof(struct local));
> +
> + l->rx = 0;
> + l->omr |= OMR_FD | OMR_TEN | OMR_REN;
> +
> +#if 1
> + /* reset PHY (cobalt quirk from if_tlp_pci.c) */
> + CSR_WRITE(l, TLP_CSR15, SIAGEN_CWE | SIAGEN_MD0);
> + delay(10);
> + CSR_WRITE(l, TLP_CSR15, SIAGEN_CWE);
> + delay(10);
> +#endif
> +
> + /* start Tx/Rx */
> + CSR_WRITE(l, TLP_OMR, l->omr);
> + CSR_WRITE(l, TLP_TPD, TPD_POLL);
> + CSR_WRITE(l, TLP_RPD, RPD_POLL);
> +
> + return l;
> +}
> +
> +int
> +tlp_send(void *dev, char *buf, u_int len)
> +{
> + struct local *l = dev;
> + struct desc *TxD;
> + u_int loop;
> +
> +#if 1
> + wbinv(buf, len);
> + TxD = &l->TxD;
> + TxD->xd3 = htole32(VTOPHYS(TxD));
> + TxD->xd2 = htole32(VTOPHYS(buf));
> + TxD->xd1 = htole32(T1_FS | T1_LS | T1_TER | (len & T1_TBS_MASK));
> +#else
> + memcpy(l->txstore, buf, len);
> + wb(l->txstore, len);
> + TxD = &l->TxD;
> + TxD->xd3 = htole32(VTOPHYS(TxD));
> + TxD->xd2 = htole32(VTOPHYS(l->txstore));
> + TxD->xd1 = htole32(T1_FS | T1_LS | T1_TER | (len & T1_TBS_MASK));
> +#endif
> + TxD->xd0 = htole32(T0_OWN);
> + wbinv(TxD, sizeof(struct desc));
> + CSR_WRITE(l, TLP_TPD, TPD_POLL);
> + loop = 100;
> + do {
> + if ((le32toh(TxD->xd0) & T0_OWN) == 0)
> + goto done;
> + DELAY(10);
> + inv(TxD, sizeof(struct desc));
> + } while (--loop > 0);
> + printf("xmit failed\n");
> + return -1;
> + done:
> + return len;
> +}
> +
> +int
> +tlp_recv(void *dev, char *buf, u_int maxlen, u_int timo)
> +{
> + struct local *l = dev;
> + struct desc *RxD;
> + u_int bound, rxstat, len;
> + uint8_t *ptr;
> +
> + bound = 1000 * timo;
> +#if 0
> +printf("recving with %u sec. timeout\n", timo);
> +#endif
> +
> + again:
> + RxD = &l->RxD[l->rx];
> + do {
> + inv(RxD, sizeof(struct desc));
> + rxstat = le32toh(RxD->xd0);
> + if ((rxstat & R0_OWN) == 0)
> + goto gotone;
> + DELAY(1000); /* 1 milli second */
> + } while (--bound > 0);
> + errno = 0;
> + CSR_WRITE(l, TLP_RPD, RPD_POLL);
> + return -1;
> + gotone:
> + if (rxstat & R0_ES) {
> + RxD->xd0 = htole32(R0_OWN);
> + wbinv(RxD, sizeof(struct desc));
> + l->rx = NEXT_RXBUF(l->rx);
> + CSR_WRITE(l, TLP_RPD, RPD_POLL);
> + goto again;
> + }
> + /* good frame */
> + len = ((rxstat & R0_FL_MASK) >> 16) - 4; /* HASFCS */
> + if (len > maxlen)
> + len = maxlen;
> + ptr = l->rxstore[l->rx];
> + memcpy(buf, ptr, len);
> + inv(ptr, len);
> + RxD->xd0 = htole32(R0_OWN);
> + wbinv(RxD, sizeof(struct desc));
> + l->rx = NEXT_RXBUF(l->rx);
> + CSR_WRITE(l, TLP_OMR, l->omr); /* necessary? */
> + return len;
> +}
> +
> +static void
> +size_srom(struct local *l)
> +{
> + /* determine 8/6 bit addressing SEEPROM */
> + l->sromsft = 8;
> + l->sromsft = (read_srom(l, 255) & 0x40000) ? 8 : 6;
> +}
> +
> +/*
> + * bare SEEPROM access with bitbang'ing
> + */
> +#define R110 6 /* SEEPROM read op */
> +#define CS (1U << 0) /* hold chip select */
> +#define CLK (1U << 1) /* clk bit */
> +#define D1 (1U << 2) /* bit existence */
> +#define D0 0 /* bit absence */
> +#define VV (1U << 3) /* taken 0/1 from SEEPROM */
> +
> +static int
> +read_srom(struct local *l, int off)
> +{
> + u_int idx, val, x1, x0, cnt, bit, ret;
> +
> + idx = off & 0xff; /* A7-A0 */
> + idx |= R110 << l->sromsft; /* 110 for READ */
> +
> + val = SROM_RD | SROM_SR;
> + CSR_WRITE(l, TLP_APROM, val);
> + val |= CS; /* hold CS */
> + CSR_WRITE(l, TLP_APROM, val);
> +
> + x1 = val | D1; /* 1 */
> + x0 = val | D0; /* 0 */
> + /* instruct R110 op. at off in MSB first order */
> + for (cnt = (1 << (l->sromsft + 2)); cnt > 0; cnt >>= 1) {
> + bit = (idx & cnt) ? x1 : x0;
> + CSR_WRITE(l, TLP_APROM, bit);
> + DELAY(10);
> + CSR_WRITE(l, TLP_APROM, bit | CLK);
> + DELAY(10);
> + }
> + /* read 16bit quantity in MSB first order */
> + ret = 0;
> + for (cnt = 16; cnt > 0; cnt--) {
> + CSR_WRITE(l, TLP_APROM, val);
> + DELAY(10);
> + CSR_WRITE(l, TLP_APROM, val | CLK);
> + DELAY(10);
> + ret = (ret << 1) | !!(CSR_READ(l, TLP_APROM) & VV);
> + }
> + val &= ~CS; /* turn off chip select */
> + CSR_WRITE(l, TLP_APROM, val);
> +
> + return ret;
> +}
> +
> +#if 0
> +
> +static u_int
> +tlp_mii_read(struct local *l, int phy, int reg)
> +{
> + /* later ... */
> + return 0;
> +}
> +
> +static void
> +tlp_mii_write(struct local *l, int phy, int reg, int val)
> +{
> + /* later ... */
> +}
> +
> +#define MII_BMCR 0x00 /* Basic mode control register (rw) */
> +#define BMCR_RESET 0x8000 /* reset */
> +#define BMCR_AUTOEN 0x1000 /* autonegotiation enable */
> +#define BMCR_ISO 0x0400 /* isolate */
> +#define BMCR_STARTNEG 0x0200 /* restart autonegotiation */
> +#define MII_BMSR 0x01 /* Basic mode status register (ro) */
> +
> +static void
> +mii_initphy(struct local *l)
> +{
> + int phy, ctl, sts, bound;
> +
> + for (phy = 0; phy < 32; phy++) {
> + ctl = tlp_mii_read(l, phy, MII_BMCR);
> + sts = tlp_mii_read(l, phy, MII_BMSR);
> + if (ctl != 0xffff && sts != 0xffff)
> + goto found;
> + }
> + printf("MII: no PHY found\n");
> + return;
> + found:
> + ctl = tlp_mii_read(l, phy, MII_BMCR);
> + tlp_mii_write(l, phy, MII_BMCR, ctl | BMCR_RESET);
> + bound = 100;
> + do {
> + DELAY(10);
> + ctl = tlp_mii_read(l, phy, MII_BMCR);
> + if (ctl == 0xffff) {
> + printf("MII: PHY %d has died after reset\n", phy);
> + return;
> + }
> + } while (bound-- > 0 && (ctl & BMCR_RESET));
> + if (bound == 0) {
> + printf("PHY %d reset failed\n", phy);
> + }
> + ctl &= ~BMCR_ISO;
> + tlp_mii_write(l, phy, MII_BMCR, ctl);
> + sts = tlp_mii_read(l, phy, MII_BMSR) |
> + tlp_mii_read(l, phy, MII_BMSR); /* read twice */
> + l->phy = phy;
> + l->bmsr = sts;
> +}
> +
> +static void
> +mii_dealan(struct local *, u_int timo)
> +{
> + u_int anar, bound;
> +
> + anar = ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA;
> + tlp_mii_write(l, l->phy, MII_ANAR, anar);
> + tlp_mii_write(l, l->phy, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
> + l->anlpar = 0;
> + bound = getsecs() + timo;
> + do {
> + l->bmsr = tlp_mii_read(l, l->phy, MII_BMSR) |
> + tlp_mii_read(l, l->phy, MII_BMSR); /* read twice */
> + if ((l->bmsr & BMSR_LINK) && (l->bmsr & BMSR_ACOMP)) {
> + l->anlpar = tlp_mii_read(l, l->phy, MII_ANLPAR);
> + break;
> + }
> + DELAY(10 * 1000);
> + } while (getsecs() < bound);
> + return;
> +}
> +#endif
>
--
"Too bad $VOLUNTEERS don't get their act together and provide
$SOLUTION_TO_VERY_DIFFICULT_PROBLEM in a decent fashion" -- from IRC,
#netbsd, EFNet