From c222d1afc42f94d202d8d3098a4705aeea18bbd3 Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Mon, 7 Aug 2006 23:35:50 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'RELENG_6'. --- etc/rc.d/mdconfig | 200 ++ etc/rc.d/mdconfig2 | 231 ++ gnu/usr.bin/binutils/libopcodes/Makefile.arm | 4 + .../en_US.ISO8859-1/hardware/powerpc/Makefile | 22 + .../hardware/powerpc/article.sgml | 31 + .../hardware/powerpc/proc-powerpc.sgml | 9 + .../installation/powerpc/Makefile | 20 + .../installation/powerpc/article.sgml | 26 + share/man/man4/stge.4 | 229 ++ sys/crypto/via/padlock.h | 87 + sys/crypto/via/padlock_cipher.c | 269 ++ sys/crypto/via/padlock_hash.c | 389 +++ sys/dev/mii/gentbi.c | 314 ++ sys/dev/mii/ip1000phy.c | 444 +++ sys/dev/mii/ip1000phyreg.h | 141 + sys/dev/stge/if_stge.c | 2698 +++++++++++++++++ sys/dev/stge/if_stgereg.h | 697 +++++ sys/geom/eli/g_eli_integrity.c | 531 ++++ sys/modules/stge/Makefile | 9 + usr.sbin/sade/globals.c | 84 + usr.sbin/sade/list.h | 55 + usr.sbin/sade/wizard.c | 200 ++ 22 files changed, 6690 insertions(+) create mode 100644 etc/rc.d/mdconfig create mode 100644 etc/rc.d/mdconfig2 create mode 100644 gnu/usr.bin/binutils/libopcodes/Makefile.arm create mode 100644 release/doc/en_US.ISO8859-1/hardware/powerpc/Makefile create mode 100644 release/doc/en_US.ISO8859-1/hardware/powerpc/article.sgml create mode 100644 release/doc/en_US.ISO8859-1/hardware/powerpc/proc-powerpc.sgml create mode 100644 release/doc/en_US.ISO8859-1/installation/powerpc/Makefile create mode 100644 release/doc/en_US.ISO8859-1/installation/powerpc/article.sgml create mode 100644 share/man/man4/stge.4 create mode 100644 sys/crypto/via/padlock.h create mode 100644 sys/crypto/via/padlock_cipher.c create mode 100644 sys/crypto/via/padlock_hash.c create mode 100644 sys/dev/mii/gentbi.c create mode 100644 sys/dev/mii/ip1000phy.c create mode 100644 sys/dev/mii/ip1000phyreg.h create mode 100644 sys/dev/stge/if_stge.c create mode 100644 sys/dev/stge/if_stgereg.h create mode 100644 sys/geom/eli/g_eli_integrity.c create mode 100644 sys/modules/stge/Makefile create mode 100644 usr.sbin/sade/globals.c create mode 100644 usr.sbin/sade/list.h create mode 100644 usr.sbin/sade/wizard.c diff --git a/etc/rc.d/mdconfig b/etc/rc.d/mdconfig new file mode 100644 index 000000000000..5a7fad6d921a --- /dev/null +++ b/etc/rc.d/mdconfig @@ -0,0 +1,200 @@ +#!/bin/sh +# +# Copyright (c) 2006 The FreeBSD Project +# 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 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 AUTHOR 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. +# +# $FreeBSD$ +# + +# PROVIDE: mdconfig +# REQUIRE: localswap +# BEFORE: mountcritlocal + +. /etc/rc.subr + +name="mdconfig" +stop_cmd="mdconfig_stop" +start_cmd="mdconfig_start" + +is_readonly() +{ + local _mp _ret + + _mp=$1 + _ret=`mount | while read _line; do + case ${_line} in + *" ${_mp} "*read-only*) + echo "yes" + ;; + + *) + ;; + esac; + done` + + if [ -n "${_ret}" ]; then + return 0 + else + return 1 + fi +} + +init_variables() +{ + local _i + + _fs="" + _mp="" + _dev="/dev/${_md}" + eval _config=\$mdconfig_${_md} + eval _newfs=\$mdconfig_${_md}_newfs + + _type=${_config##*-t\ } + _type=${_type%%\ *} + if [ -z "${_type}" ]; then + err 1 "You need to specify \"-t \" in mdconfig_${_md}" + fi + + if [ "${_type}" = "vnode" ]; then + _file=${_config##*-f\ } + _file=${_file%%\ *} + if [ -z "${_file}" ]; then + err 2 "You need to specify \"-f \" in mdconfig_${_md} for vnode devices" + fi + if [ "${_file}" != "${_file%.uzip}" ]; then + # Load geom_uzip kernel module if needed + kldstat -q -m g_uzip || kldload geom_uzip || err 1 "geom_uzip failed to load." + _dev="/dev/${_md}.uzip" + fi + for _i in `df ${_file} 2>/dev/null`; do _fs=${_i}; done + fi + + # Debugging help. + debug "${_md} config: ${_config}" + debug "${_md} type: ${_type}" + debug "${_md} dev: ${_dev}" + debug "${_md} file: ${_file}" + debug "${_md} fs: ${_fs}" + debug "${_md} newfs flags: ${_newfs}" +} + +mdconfig_start() +{ + local _md _mp _config _type _dev _file _fs _newfs _fsck_cmd + + # If there are no devices return before loading geom_md.ko. + if [ -z "${_mdconfig_list}" ]; then + return + fi + + kldstat -q -m g_md || kldload geom_md || err 1 "geom_md failed to load." + + for _md in ${_mdconfig_list}; do + init_variables ${_md} + # Create md(4) devices of types swap, malloc and vnode if the + # file is on the root partition. + if [ "${_type}" != "vnode" -o "${_fs}" = "/" ]; then + if [ "${_type}" = "vnode" ]; then + if is_readonly ${_fs}; then + warn "${_fs} is mounted read-only, skipping ${_md}." + continue + fi + fi + if mdconfig -l -u ${_md} >/dev/null 2>&1; then + err 3 "${_md} already exists" + fi + echo "Creating ${_md} device (${_type})." + if ! mdconfig -a ${_config} -u ${_md}; then + echo "Creating ${_md} device failed, moving on." + continue + fi + # Skip fsck for uzip devices. + if [ "${_type}" = "vnode" ]; then + if [ "${_file}" != "${_file%.uzip}" ]; then + _fsck_cmd=":" + elif checkyesno background_fsck; then + _fsck_cmd="fsck -F" + else + _fsck_cmd="fsck" + fi + if ! eval ${_fsck_cmd} -p ${_dev} >/dev/null; then + echo "Fsck failed on ${_dev}, not mounting the filesystem." + continue + + fi + else + newfs ${_newfs} ${_dev} >/dev/null + fi + if mount -d ${_dev} 2>&1 >/dev/null; then + echo "Mounting ${_dev}." + mount ${_dev} + fi + fi + done +} + +mdconfig_stop() +{ + local _md _mp _config _type _dev _file _fs _newfs _i + + for _md in ${_mdconfig_list}; do + init_variables ${_md} + if [ "${_type}" != "vnode" -o "${_fs}" = "/" ]; then + for _i in `df ${_dev} 2>/dev/null`; do _mp=${_i}; done + if [ -z "${_mp}" -o "${_mp}" != "${_mp%%%}" ]; then + echo "Device ${_dev} isn't mounted." + else + echo "Umounting ${_dev}." + umount ${_dev} + fi + if mdconfig -l -u ${_md} >/dev/null 2>&1; then + echo "Destroying ${_md}." + mdconfig -d -u ${_md} + fi + fi + done +} + +_mdconfig_cmd="$1" +if [ $# -gt 0 ]; then + shift +fi +[ -n "$*" ] && _mdconfig_list="$*" + +load_rc_config $name + +_mdconfig_unit=0 +if [ -z "${_mdconfig_list}" ]; then + while :; do + eval _mdconfig_config=\$mdconfig_md${_mdconfig_unit} + if [ -z "${_mdconfig_config}" ]; then + break + else + _mdconfig_list="${_mdconfig_list}${_mdconfig_list:+ }md${_mdconfig_unit}" + _mdconfig_unit=$((${_mdconfig_unit} + 1)) + fi + done +fi + +run_rc_command "${_mdconfig_cmd}" diff --git a/etc/rc.d/mdconfig2 b/etc/rc.d/mdconfig2 new file mode 100644 index 000000000000..717876590796 --- /dev/null +++ b/etc/rc.d/mdconfig2 @@ -0,0 +1,231 @@ +#!/bin/sh +# +# Copyright (c) 2006 The FreeBSD Project +# 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 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 AUTHOR 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. +# +# $FreeBSD$ +# + +# PROVIDE: mdconfig2 +# REQUIRE: mountcritremote +# BEFORE: SERVERS + +. /etc/rc.subr + +name="mdconfig2" +stop_cmd="mdconfig2_stop" +start_cmd="mdconfig2_start" + +is_readonly() +{ + local _mp _ret + + _mp=$1 + _ret=`mount | while read _line; do + case ${_line} in + *" ${_mp} "*read-only*) + echo "yes" + ;; + + *) + ;; + esac; + done` + + if [ -n "${_ret}" ]; then + return 0 + else + return 1 + fi +} + +init_variables() +{ + local _i + + _fs="" + _mp="" + _mounted="no" + _dev="/dev/${_md}" + eval _config=\$mdconfig_${_md} + eval _owner=\$mdconfig_${_md}_owner + eval _perms=\$mdconfig_${_md}_perms + eval _files=\$mdconfig_${_md}_files + eval _populate=\$mdconfig_${_md}_cmd + + _type=${_config##*-t\ } + _type=${_type%%\ *} + if [ -z "${_type}" ]; then + err 1 "You need to specify \"-t \" in mdconfig_${_md}" + fi + + if [ "${_type}" = "vnode" ]; then + _file=${_config##*-f\ } + _file=${_file%%\ *} + if [ -z "${_file}" ]; then + err 2 "You need to specify \"-f \" in mdconfig_${_md} for vnode devices" + fi + + if [ "${_file}" != "${_file%.uzip}" ]; then + # Load geom_uzip kernel module if needed + kldstat -q -m g_uzip || kldload geom_uzip || err 1 "geom_uzip failed to load." + _dev="/dev/${_md}.uzip" + fi + for _i in `df ${_file} 2>/dev/null`; do _fs=${_i}; done + fi + + # Debugging help. + debug "${_md} config: ${_config}" + debug "${_md} type: ${_type}" + debug "${_md} dev: ${_dev}" + debug "${_md} file: ${_file}" + debug "${_md} fs: ${_fs}" + debug "${_md} owner: ${_owner}" + debug "${_md} perms: ${_perms}" + debug "${_md} files: ${_files}" + debug "${_md} populate cmd: ${_populate}" +} + +mdconfig2_start() +{ + local _md _fs _mp _mounted _dev _config _type _file _owner _perms _files _populate _fsck_cmd _i + + # If there are no devices return before loading geom_md.ko. + if [ -z "${_mdconfig2_list}" ]; then + return + fi + + kldstat -q -m g_md || kldload geom_md || err 1 "geom_md failed to load." + + for _md in ${_mdconfig2_list}; do + init_variables ${_md} + if [ ! -r ${_file} ]; then + err 3 "${_file} doesn't exist" + continue + fi + # First pass: create md(4) vnode devices from files stored on + # non-root partition. Swap and malloc md(4) devices have already + # been created. + if [ "${_type}" = "vnode" -a "${_fs}" != "/" ]; then + if is_readonly ${_fs}; then + warn "${_fs} is mounted read-only, skipping ${_md}." + continue + fi + if mdconfig -l -u ${_md} >/dev/null 2>&1; then + err 3 "${_md} already exists" + fi + echo "Creating ${_md} device (${_type})." + if ! mdconfig -a ${_config} -u ${_md}; then + echo "Creating ${_md} device failed, moving on." + continue + fi + # Skip fsck for uzip devices. + if [ "${_file}" != "${_file%.uzip}" ]; then + _fsck_cmd=":" + elif checkyesno background_fsck; then + _fsck_cmd="fsck -F" + else + _fsck_cmd="fsck" + fi + if ! eval ${_fsck_cmd} -p ${_dev} >/dev/null; then + echo "Fsck failed on ${_dev}, not mounting the filesystem." + continue + fi + if mount -d ${_dev} >/dev/null 2>&1; then + echo "Mounting ${_dev}." + mount ${_dev} + fi + fi + + for _i in `df ${_dev} 2>/dev/null`; do _mp=${_i}; done + if [ ! -z "${_mp}" -a "${_mp}" = "${_mp%%%}" ]; then + _mounted="yes" + fi + + if checkyesno _mounted; then + # Second pass: change permissions and ownership. + [ -z "${_owner}" ] || chown -f ${_owner} ${_dev} ${_mp} + [ -z "${_perms}" ] || chmod -f ${_perms} ${_dev} ${_mp} + + # Third pass: populate with foreign files. + if [ -n "${_files}" -o -n "${_populate}" ]; then + echo "Populating ${_dev}." + fi + if [ -n "${_files}" ]; then + cp -Rp ${_files} ${_mp} + fi + if [ -n "${_populate}" ]; then + eval ${_populate} + fi + fi + done +} + +mdconfig2_stop() +{ + local _md _fs _mp _mounted _dev _config _type _file _owner _perms _files _populate + + for _md in ${_mdconfig2_list}; do + init_variables ${_md} + if [ "${_type}" = "vnode" ]; then + for i in `df ${_dev} 2>/dev/null`; do _mp=$i; done + if [ ! -r "${_file}" -o "${_fs}" = "/" ]; then + continue + fi + if [ -z "${_mp}" -o "${_mp}" != "${_mp%%%}" ]; then + echo "Device ${_dev} isn't mounted." + else + echo "Umounting ${_dev}." + umount ${_dev} + fi + if mdconfig -l -u ${_md} >/dev/null 2>&1; then + echo "Destroying ${_md}." + mdconfig -d -u ${_md} + fi + fi + done +} + +_mdconfig2_cmd="$1" +if [ $# -gt 0 ]; then + shift +fi +[ -n "$*" ] && _mdconfig2_list="$*" + +load_rc_config $name + +_mdconfig2_unit=0 +if [ -z "${_mdconfig2_list}" ]; then + while :; do + eval _mdconfig2_config=\$mdconfig_md${_mdconfig2_unit} + if [ -z "${_mdconfig2_config}" ]; then + break + else + _mdconfig2_list="${_mdconfig2_list}${_mdconfig2_list:+ }md${_mdconfig2_unit}" + _mdconfig2_unit=$((${_mdconfig2_unit} + 1)) + fi + done +fi + +run_rc_command "${_mdconfig2_cmd}" diff --git a/gnu/usr.bin/binutils/libopcodes/Makefile.arm b/gnu/usr.bin/binutils/libopcodes/Makefile.arm new file mode 100644 index 000000000000..67bb751db477 --- /dev/null +++ b/gnu/usr.bin/binutils/libopcodes/Makefile.arm @@ -0,0 +1,4 @@ +# $FreeBSD$ + +SRCS+= arm-dis.c +CFLAGS+= -DARCH_arm diff --git a/release/doc/en_US.ISO8859-1/hardware/powerpc/Makefile b/release/doc/en_US.ISO8859-1/hardware/powerpc/Makefile new file mode 100644 index 000000000000..04a1c03ccac1 --- /dev/null +++ b/release/doc/en_US.ISO8859-1/hardware/powerpc/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ + +RELN_ROOT?= ${.CURDIR}/../../.. + +DOC?= article +FORMATS?= html +INSTALL_COMPRESSED?= gz +INSTALL_ONLY_COMPRESSED?= + +JADEFLAGS+= -V %generate-article-toc% + +# SGML content +SRCS+= article.sgml +SRCS+= proc-powerpc.sgml +SRCS+= ../common/hw.ent +SRCS+= ../common/artheader.sgml +SRCS+= ../common/dev.sgml +SRCS+= ${DEV-AUTODIR}/catalog-auto +SRCS+= ${DEV-AUTODIR}/dev-auto.sgml + +.include "${RELN_ROOT}/share/mk/doc.relnotes.mk" +.include "${DOC_PREFIX}/share/mk/doc.project.mk" diff --git a/release/doc/en_US.ISO8859-1/hardware/powerpc/article.sgml b/release/doc/en_US.ISO8859-1/hardware/powerpc/article.sgml new file mode 100644 index 000000000000..b722ade97047 --- /dev/null +++ b/release/doc/en_US.ISO8859-1/hardware/powerpc/article.sgml @@ -0,0 +1,31 @@ + + + +%articles.ent; + + +%release; + +%devauto; + %sections; + + + + + + +]> + +
+ &artheader; + + + This is a preliminary document. It is incomplete, and in + need of additional content. + + + §.intro; + §.proc.powerpc; + §.dev; +
diff --git a/release/doc/en_US.ISO8859-1/hardware/powerpc/proc-powerpc.sgml b/release/doc/en_US.ISO8859-1/hardware/powerpc/proc-powerpc.sgml new file mode 100644 index 000000000000..e21a0eff9976 --- /dev/null +++ b/release/doc/en_US.ISO8859-1/hardware/powerpc/proc-powerpc.sgml @@ -0,0 +1,9 @@ + + + Supported Processors and Motherboards + + The information for this paragraph has yet to be compiled. + + diff --git a/release/doc/en_US.ISO8859-1/installation/powerpc/Makefile b/release/doc/en_US.ISO8859-1/installation/powerpc/Makefile new file mode 100644 index 000000000000..36f855e86d97 --- /dev/null +++ b/release/doc/en_US.ISO8859-1/installation/powerpc/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +RELN_ROOT?= ${.CURDIR}/../../.. + +DOC?= article +FORMATS?= html +INSTALL_COMPRESSED?= gz +INSTALL_ONLY_COMPRESSED?= + +# SGML content +SRCS+= article.sgml +SRCS+= ../common/artheader.sgml +SRCS+= ../common/abstract.sgml +SRCS+= ../common/install.sgml +SRCS+= ../common/layout.sgml +SRCS+= ../common/trouble.sgml +SRCS+= ../common/upgrade.sgml + +.include "${RELN_ROOT}/share/mk/doc.relnotes.mk" +.include "${DOC_PREFIX}/share/mk/doc.project.mk" diff --git a/release/doc/en_US.ISO8859-1/installation/powerpc/article.sgml b/release/doc/en_US.ISO8859-1/installation/powerpc/article.sgml new file mode 100644 index 000000000000..bfe0110880be --- /dev/null +++ b/release/doc/en_US.ISO8859-1/installation/powerpc/article.sgml @@ -0,0 +1,26 @@ + + + +%articles.ent; + + +%release; + +%sections; + + + + + + +]> + +
+&artheader; +&abstract; +§.install; +§.layout; +§.upgrade; +§.trouble; +
diff --git a/share/man/man4/stge.4 b/share/man/man4/stge.4 new file mode 100644 index 000000000000..95ea0b971765 --- /dev/null +++ b/share/man/man4/stge.4 @@ -0,0 +1,229 @@ +.\" $NetBSD: stge.4,v 1.7 2003/02/14 15:20:20 grant Exp $ +.\" +.\" Copyright (c) 2001 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd July 25, 2006 +.Dt STGE 4 +.Os +.Sh NAME +.Nm stge +.Nd Sundance/Tamarack TC9021 Gigabit Ethernet adapter driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device miibus" +.Cd "device stge" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_stge_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +device driver provides support for various NICs based on the +Sundance/Tamarack TC9021 Gigabit Ethernet controller chip. +.Pp +The Sundance/Tamarack TC9021 is found on the D-Link DGE-550T +and the Antares Microsystems Gigabit Ethernet board. +It uses an external PHY or an external 10-bit interface. +.Pp +All NICs supported by the +.Nm +driver have TCP/UDP/IP checksum offload for both receive and +transmit, hardware VLAN tag stripping/insertion features, and +receive interrupt moderation mechanism as well as a 64-bit +multicast hash filter. +The Sundance/Tamarack TC9021 supports TBI (ten bit interface) +and GMII transceivers, which means it can be used with either +copper or 1000baseX fiber applications. +.Pp +The Sundance/Tamarack TC9021 also support jumbo frames, which can be +configured via the interface MTU setting. +Selecting an MTU larger than 1500 bytes with the +.Xr ifconfig 8 +utility configures the adapter to receive and transmit jumbo frames. +.Pp +The +.Nm +driver supports the following media types: +.Bl -tag -width ".Cm 10baseT/UTP" +.It Cm autoselect +Enable autoselection of the media type and options. +The user can manually override +the autoselected mode by adding media options to +.Xr rc.conf 5 . +.It Cm 10baseT/UTP +Set 10Mbps operation. +The +.Xr ifconfig 8 +.Cm mediaopt +option can also be used to select either +.Cm full-duplex +or +.Cm half-duplex +modes. +.It Cm 100baseTX +Set 100Mbps (Fast Ethernet) operation. +The +.Xr ifconfig 8 +.Cm mediaopt +option can also be used to select either +.Cm full-duplex +or +.Cm half-duplex +modes. +.It Cm 1000baseTX +Set 1000baseTX operation over twisted pair. +The Sundance/Tamarack supports 1000Mbps in +.Cm autoselect +mode only. +.\" .It Cm 1000baseSX +.\" Set 1000Mbps (Gigabit Ethernet) operation. +.\" Both +.\" .Cm full-duplex +.\" and +.\" .Cm half-duplex +.\" modes are supported. +.El +.Pp +The +.Nm +driver supports the following media options: +.Bl -tag -width ".Cm full-duplex" +.It Cm full-duplex +Force full duplex operation. +.It Cm half-duplex +Force half duplex operation. +.El +.Pp +The +.Nm +driver also supports one special link option for 1000baseTX cards: +.Pp +.Bl -tag -width ".Cm link0" +.It Cm link0 +With 1000baseTX cards, establishing a link between two ports requires +that one port is configured as master and the other one as slave. +With autonegotiation, +the master/slave settings will be chosen automatically. +However when manually selecting the link state, it is necessary to +force one side of the link to be a master and the other a slave. +The +.Nm +driver configures the ports as slaves by default. +Setting the +.Ar link0 +flag with +.Xr ifconfig 8 +will set a port as a master instead. +.El +.Pp +For more information on configuring this device, see +.Xr ifconfig 8 . +.Sh HARDWARE +The +.Nm +driver provides support for various NICs based on the Sundance/Tamarack +TC9021 based Gigabit Ethernet controller chips, including: +.Pp +.Bl -bullet -compact +.It +Antares Microsystems Gigabit Ethernet +.It +ASUS NX1101 Gigabit Ethernet +.It +D-Link DL-4000 Gigabit Ethernet +.It +IC Plus IP1000A Gigabit Ethernet +.It +Sundance ST-2021 Gigabit Ethernet +.It +Sundance ST-2023 Gigabit Ethernet +.It +Sundance TC9021 Gigabit Ethernet +.It +Tamarack TC9021 Gigabit Ethernet +.El +.Sh SYSCTL VARIABLES +The following variables are available as both +.Xr sysctl 8 +variables and +.Xr loader 8 +tunables: +.Bl -tag -width indent +.It Va dev.stge.%d.rxint_nframe +Number of frames between RxDMAComplete interrupt. +The accepted range is 1 to 255, default value is 8 frames. +The interface has to be brought down and up again before a change takes effect. +.It Va dev.stge.%d.rxint_dmawait +Maximum amount of time to wait in 1us increments before issuing +a Rx interrupt if number of frames received is less than rxint_nframe. +The accepted range is 0 to 4194, default value is 30 microseconds. +The interface has to be brought down and up again before a change takes effect. +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr miibus 4 , +.Xr netintro 4 , +.Xr ng_ether 4 , +.Xr polling 4 , +.Xr vlan 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +driver was ported from +.Nx +and first appeared in +.Fx 6.2 . +The +.Nx +version was written by +.An Jason R. Thorpe +.Aq thorpej@NetBSD.org . +.Sh AUTHORS +The +.Nm +driver was ported by +.An Pyun YongHyeon +.Aq yongari@FreeBSD.org . diff --git a/sys/crypto/via/padlock.h b/sys/crypto/via/padlock.h new file mode 100644 index 000000000000..7d928ba1719c --- /dev/null +++ b/sys/crypto/via/padlock.h @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _PADLOCK_H_ +#define _PADLOCK_H_ + +#include +#include + +union padlock_cw { + uint64_t raw; + struct { + u_int round_count : 4; + u_int algorithm_type : 3; + u_int key_generation : 1; + u_int intermediate : 1; + u_int direction : 1; + u_int key_size : 2; + u_int filler0 : 20; + u_int filler1 : 32; + u_int filler2 : 32; + u_int filler3 : 32; + } __field; +}; +#define cw_round_count __field.round_count +#define cw_algorithm_type __field.algorithm_type +#define cw_key_generation __field.key_generation +#define cw_intermediate __field.intermediate +#define cw_direction __field.direction +#define cw_key_size __field.key_size +#define cw_filler0 __field.filler0 +#define cw_filler1 __field.filler1 +#define cw_filler2 __field.filler2 +#define cw_filler3 __field.filler3 + +struct padlock_session { + union padlock_cw ses_cw __aligned(16); + uint32_t ses_ekey[4 * (RIJNDAEL_MAXNR + 1) + 4] __aligned(16); /* 128 bit aligned */ + uint32_t ses_dkey[4 * (RIJNDAEL_MAXNR + 1) + 4] __aligned(16); /* 128 bit aligned */ + uint8_t ses_iv[16] __aligned(16); /* 128 bit aligned */ + struct auth_hash *ses_axf; + uint8_t *ses_ictx; + uint8_t *ses_octx; + int ses_mlen; + int ses_used; + uint32_t ses_id; + TAILQ_ENTRY(padlock_session) ses_next; +}; + +#define PADLOCK_ALIGN(p) (void *)(roundup2((uintptr_t)(p), 16)) + +int padlock_cipher_setup(struct padlock_session *ses, + struct cryptoini *encini); +int padlock_cipher_process(struct padlock_session *ses, + struct cryptodesc *enccrd, struct cryptop *crp); +int padlock_hash_setup(struct padlock_session *ses, + struct cryptoini *macini); +int padlock_hash_process(struct padlock_session *ses, + struct cryptodesc *maccrd, struct cryptop *crp); +void padlock_hash_free(struct padlock_session *ses); + +#endif /* !_PADLOCK_H_ */ diff --git a/sys/crypto/via/padlock_cipher.c b/sys/crypto/via/padlock_cipher.c new file mode 100644 index 000000000000..698eb8bd5889 --- /dev/null +++ b/sys/crypto/via/padlock_cipher.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * Copyright (c) 2004 Mark R V Murray + * 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 AUTHORS 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 AUTHORS 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. + */ + +/* $OpenBSD: via.c,v 1.3 2004/06/15 23:36:55 deraadt Exp $ */ +/*- + * Copyright (c) 2003 Jason Wright + * Copyright (c) 2003, 2004 Theo de Raadt + * All rights reserved. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define PADLOCK_ROUND_COUNT_AES128 10 +#define PADLOCK_ROUND_COUNT_AES192 12 +#define PADLOCK_ROUND_COUNT_AES256 14 + +#define PADLOCK_ALGORITHM_TYPE_AES 0 + +#define PADLOCK_KEY_GENERATION_HW 0 +#define PADLOCK_KEY_GENERATION_SW 1 + +#define PADLOCK_DIRECTION_ENCRYPT 0 +#define PADLOCK_DIRECTION_DECRYPT 1 + +#define PADLOCK_KEY_SIZE_128 0 +#define PADLOCK_KEY_SIZE_192 1 +#define PADLOCK_KEY_SIZE_256 2 + +MALLOC_DECLARE(M_PADLOCK); + +static __inline void +padlock_cbc(void *in, void *out, size_t count, void *key, union padlock_cw *cw, + void *iv) +{ +#ifdef __GNUCLIKE_ASM + /* The .byte line is really VIA C3 "xcrypt-cbc" instruction */ + __asm __volatile( + "pushf \n\t" + "popf \n\t" + "rep \n\t" + ".byte 0x0f, 0xa7, 0xd0" + : "+a" (iv), "+c" (count), "+D" (out), "+S" (in) + : "b" (key), "d" (cw) + : "cc", "memory" + ); +#endif +} + +static void +padlock_cipher_key_setup(struct padlock_session *ses, caddr_t key, int klen) +{ + union padlock_cw *cw; + int i; + + cw = &ses->ses_cw; + if (cw->cw_key_generation == PADLOCK_KEY_GENERATION_SW) { + /* Build expanded keys for both directions */ + rijndaelKeySetupEnc(ses->ses_ekey, key, klen); + rijndaelKeySetupDec(ses->ses_dkey, key, klen); + for (i = 0; i < 4 * (RIJNDAEL_MAXNR + 1); i++) { + ses->ses_ekey[i] = ntohl(ses->ses_ekey[i]); + ses->ses_dkey[i] = ntohl(ses->ses_dkey[i]); + } + } else { + bcopy(key, ses->ses_ekey, klen); + bcopy(key, ses->ses_dkey, klen); + } +} + +int +padlock_cipher_setup(struct padlock_session *ses, struct cryptoini *encini) +{ + union padlock_cw *cw; + + if (encini->cri_klen != 128 && encini->cri_klen != 192 && + encini->cri_klen != 256) { + return (EINVAL); + } + + cw = &ses->ses_cw; + bzero(cw, sizeof(*cw)); + cw->cw_algorithm_type = PADLOCK_ALGORITHM_TYPE_AES; + cw->cw_key_generation = PADLOCK_KEY_GENERATION_SW; + cw->cw_intermediate = 0; + switch (encini->cri_klen) { + case 128: + cw->cw_round_count = PADLOCK_ROUND_COUNT_AES128; + cw->cw_key_size = PADLOCK_KEY_SIZE_128; +#ifdef HW_KEY_GENERATION + /* This doesn't buy us much, that's why it is commented out. */ + cw->cw_key_generation = PADLOCK_KEY_GENERATION_HW; +#endif + break; + case 192: + cw->cw_round_count = PADLOCK_ROUND_COUNT_AES192; + cw->cw_key_size = PADLOCK_KEY_SIZE_192; + break; + case 256: + cw->cw_round_count = PADLOCK_ROUND_COUNT_AES256; + cw->cw_key_size = PADLOCK_KEY_SIZE_256; + break; + } + if (encini->cri_key != NULL) { + padlock_cipher_key_setup(ses, encini->cri_key, + encini->cri_klen); + } + + arc4rand(ses->ses_iv, sizeof(ses->ses_iv), 0); + return (0); +} + +/* + * Function checks if the given buffer is already 16 bytes aligned. + * If it is there is no need to allocate new buffer. + * If it isn't, new buffer is allocated. + */ +static u_char * +padlock_cipher_alloc(struct cryptodesc *enccrd, struct cryptop *crp, + int *allocated) +{ + u_char *addr; + + if (crp->crp_flags & CRYPTO_F_IMBUF) + goto alloc; + else { + if (crp->crp_flags & CRYPTO_F_IOV) { + struct uio *uio; + struct iovec *iov; + + uio = (struct uio *)crp->crp_buf; + if (uio->uio_iovcnt != 1) + goto alloc; + iov = uio->uio_iov; + addr = (u_char *)iov->iov_base + enccrd->crd_skip; + } else { + addr = (u_char *)crp->crp_buf; + } + if (((uintptr_t)addr & 0xf) != 0) /* 16 bytes aligned? */ + goto alloc; + *allocated = 0; + return (addr); + } +alloc: + *allocated = 1; + addr = malloc(enccrd->crd_len + 16, M_PADLOCK, M_NOWAIT); + return (addr); +} + +int +padlock_cipher_process(struct padlock_session *ses, struct cryptodesc *enccrd, + struct cryptop *crp) +{ + union padlock_cw *cw; + u_char *buf, *abuf; + uint32_t *key; + int allocated; + + buf = padlock_cipher_alloc(enccrd, crp, &allocated); + if (buf == NULL) + return (ENOMEM); + /* Buffer has to be 16 bytes aligned. */ + abuf = PADLOCK_ALIGN(buf); + + if ((enccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) { + padlock_cipher_key_setup(ses, enccrd->crd_key, + enccrd->crd_klen); + } + + cw = &ses->ses_cw; + cw->cw_filler0 = 0; + cw->cw_filler1 = 0; + cw->cw_filler2 = 0; + cw->cw_filler3 = 0; + if ((enccrd->crd_flags & CRD_F_ENCRYPT) != 0) { + cw->cw_direction = PADLOCK_DIRECTION_ENCRYPT; + key = ses->ses_ekey; + if ((enccrd->crd_flags & CRD_F_IV_EXPLICIT) != 0) + bcopy(enccrd->crd_iv, ses->ses_iv, 16); + + if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) { + crypto_copyback(crp->crp_flags, crp->crp_buf, + enccrd->crd_inject, AES_BLOCK_LEN, ses->ses_iv); + } + } else { + cw->cw_direction = PADLOCK_DIRECTION_DECRYPT; + key = ses->ses_dkey; + if ((enccrd->crd_flags & CRD_F_IV_EXPLICIT) != 0) + bcopy(enccrd->crd_iv, ses->ses_iv, AES_BLOCK_LEN); + else { + crypto_copydata(crp->crp_flags, crp->crp_buf, + enccrd->crd_inject, AES_BLOCK_LEN, ses->ses_iv); + } + } + + if (allocated) { + crypto_copydata(crp->crp_flags, crp->crp_buf, enccrd->crd_skip, + enccrd->crd_len, abuf); + } + + padlock_cbc(abuf, abuf, enccrd->crd_len / 16, key, cw, ses->ses_iv); + + if (allocated) { + crypto_copyback(crp->crp_flags, crp->crp_buf, enccrd->crd_skip, + enccrd->crd_len, abuf); + } + + /* copy out last block for use as next session IV */ + if ((enccrd->crd_flags & CRD_F_ENCRYPT) != 0) { + crypto_copydata(crp->crp_flags, crp->crp_buf, + enccrd->crd_skip + enccrd->crd_len - AES_BLOCK_LEN, + AES_BLOCK_LEN, ses->ses_iv); + } + + if (allocated) { + bzero(buf, enccrd->crd_len + 16); + free(buf, M_PADLOCK); + } + return (0); +} diff --git a/sys/crypto/via/padlock_hash.c b/sys/crypto/via/padlock_hash.c new file mode 100644 index 000000000000..6c421032ed99 --- /dev/null +++ b/sys/crypto/via/padlock_hash.c @@ -0,0 +1,389 @@ +/*- + * Copyright (c) 2006 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#if defined(__i386__) && !defined(PC98) +#include +#include +#include +#include +#endif + +#include +#include /* for hmac_ipad_buffer and hmac_opad_buffer */ +#include + +#include + +/* + * Implementation notes. + * + * Some VIA CPUs provides SHA1 and SHA256 acceleration. + * We implement all HMAC algorithms provided by crypto(9) framework, but we do + * the crypto work in software unless this is HMAC/SHA1 or HMAC/SHA256 and + * our CPU can accelerate it. + * + * Additional CPU instructions, which preform SHA1 and SHA256 are one-shot + * functions - we have only one chance to give the data, CPU itself will add + * the padding and calculate hash automatically. + * This means, it is not possible to implement common init(), update(), final() + * methods. + * The way I've choosen is to keep adding data to the buffer on update() + * (reallocating the buffer if necessary) and call XSHA{1,256} instruction on + * final(). + */ + +struct padlock_sha_ctx { + uint8_t *psc_buf; + int psc_offset; + int psc_size; +}; +CTASSERT(sizeof(struct padlock_sha_ctx) <= sizeof(union authctx)); + +static void padlock_sha_init(struct padlock_sha_ctx *ctx); +static int padlock_sha_update(struct padlock_sha_ctx *ctx, uint8_t *buf, + uint16_t bufsize); +static void padlock_sha1_final(uint8_t *hash, struct padlock_sha_ctx *ctx); +static void padlock_sha256_final(uint8_t *hash, struct padlock_sha_ctx *ctx); + +static struct auth_hash padlock_hmac_sha1 = { + CRYPTO_SHA1_HMAC, "HMAC-SHA1", + 20, SHA1_HASH_LEN, SHA1_HMAC_BLOCK_LEN, sizeof(struct padlock_sha_ctx), + (void (*)(void *))padlock_sha_init, + (int (*)(void *, uint8_t *, uint16_t))padlock_sha_update, + (void (*)(uint8_t *, void *))padlock_sha1_final +}; + +static struct auth_hash padlock_hmac_sha256 = { + CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256", + 32, SHA2_256_HASH_LEN, SHA2_256_HMAC_BLOCK_LEN, sizeof(struct padlock_sha_ctx), + (void (*)(void *))padlock_sha_init, + (int (*)(void *, uint8_t *, uint16_t))padlock_sha_update, + (void (*)(uint8_t *, void *))padlock_sha256_final +}; + +MALLOC_DECLARE(M_PADLOCK); + +static __inline void +padlock_output_block(uint32_t *src, uint32_t *dst, size_t count) +{ + + while (count-- > 0) + *dst++ = bswap32(*src++); +} + +static void +padlock_do_sha1(const u_char *in, u_char *out, int count) +{ + u_char buf[128+16]; /* PadLock needs at least 128 bytes buffer. */ + u_char *result = PADLOCK_ALIGN(buf); + + ((uint32_t *)result)[0] = 0x67452301; + ((uint32_t *)result)[1] = 0xEFCDAB89; + ((uint32_t *)result)[2] = 0x98BADCFE; + ((uint32_t *)result)[3] = 0x10325476; + ((uint32_t *)result)[4] = 0xC3D2E1F0; + +#ifdef __GNUCLIKE_ASM + __asm __volatile( + ".byte 0xf3, 0x0f, 0xa6, 0xc8" /* rep xsha1 */ + : "+S"(in), "+D"(result) + : "c"(count), "a"(0) + ); +#endif + + padlock_output_block((uint32_t *)result, (uint32_t *)out, + SHA1_HASH_LEN / sizeof(uint32_t)); +} + +static void +padlock_do_sha256(const char *in, char *out, int count) +{ + char buf[128+16]; /* PadLock needs at least 128 bytes buffer. */ + char *result = PADLOCK_ALIGN(buf); + + ((uint32_t *)result)[0] = 0x6A09E667; + ((uint32_t *)result)[1] = 0xBB67AE85; + ((uint32_t *)result)[2] = 0x3C6EF372; + ((uint32_t *)result)[3] = 0xA54FF53A; + ((uint32_t *)result)[4] = 0x510E527F; + ((uint32_t *)result)[5] = 0x9B05688C; + ((uint32_t *)result)[6] = 0x1F83D9AB; + ((uint32_t *)result)[7] = 0x5BE0CD19; + +#ifdef __GNUCLIKE_ASM + __asm __volatile( + ".byte 0xf3, 0x0f, 0xa6, 0xd0" /* rep xsha256 */ + : "+S"(in), "+D"(result) + : "c"(count), "a"(0) + ); +#endif + + padlock_output_block((uint32_t *)result, (uint32_t *)out, + SHA2_256_HASH_LEN / sizeof(uint32_t)); +} + +static void +padlock_sha_init(struct padlock_sha_ctx *ctx) +{ + + ctx->psc_buf = NULL; + ctx->psc_offset = 0; + ctx->psc_size = 0; +} + +static int +padlock_sha_update(struct padlock_sha_ctx *ctx, uint8_t *buf, uint16_t bufsize) +{ + + if (ctx->psc_size - ctx->psc_offset < bufsize) { + ctx->psc_size = MAX(ctx->psc_size * 2, ctx->psc_size + bufsize); + ctx->psc_buf = realloc(ctx->psc_buf, ctx->psc_size, M_PADLOCK, + M_WAITOK); + } + bcopy(buf, ctx->psc_buf + ctx->psc_offset, bufsize); + ctx->psc_offset += bufsize; + return (0); +} + +static void +padlock_sha_free(struct padlock_sha_ctx *ctx) +{ + + if (ctx->psc_buf != NULL) { + //bzero(ctx->psc_buf, ctx->psc_size); + free(ctx->psc_buf, M_PADLOCK); + ctx->psc_buf = NULL; + ctx->psc_offset = 0; + ctx->psc_size = 0; + } +} + +static void +padlock_sha1_final(uint8_t *hash, struct padlock_sha_ctx *ctx) +{ + + padlock_do_sha1(ctx->psc_buf, hash, ctx->psc_offset); + padlock_sha_free(ctx); +} + +static void +padlock_sha256_final(uint8_t *hash, struct padlock_sha_ctx *ctx) +{ + + padlock_do_sha256(ctx->psc_buf, hash, ctx->psc_offset); + padlock_sha_free(ctx); +} + +static void +padlock_copy_ctx(struct auth_hash *axf, void *sctx, void *dctx) +{ + + if ((via_feature_xcrypt & VIA_HAS_SHA) != 0 && + (axf->type == CRYPTO_SHA1_HMAC || + axf->type == CRYPTO_SHA2_256_HMAC)) { + struct padlock_sha_ctx *spctx = sctx, *dpctx = dctx; + + dpctx->psc_offset = spctx->psc_offset; + dpctx->psc_size = spctx->psc_size; + dpctx->psc_buf = malloc(dpctx->psc_size, M_PADLOCK, M_WAITOK); + bcopy(spctx->psc_buf, dpctx->psc_buf, dpctx->psc_size); + } else { + bcopy(sctx, dctx, axf->ctxsize); + } +} + +static void +padlock_free_ctx(struct auth_hash *axf, void *ctx) +{ + + if ((via_feature_xcrypt & VIA_HAS_SHA) != 0 && + (axf->type == CRYPTO_SHA1_HMAC || + axf->type == CRYPTO_SHA2_256_HMAC)) { + padlock_sha_free(ctx); + } +} + +static void +padlock_hash_key_setup(struct padlock_session *ses, caddr_t key, int klen) +{ + struct auth_hash *axf; + int i; + + klen /= 8; + axf = ses->ses_axf; + + /* + * Try to free contexts before using them, because + * padlock_hash_key_setup() can be called twice - once from + * padlock_newsession() and again from padlock_process(). + */ + padlock_free_ctx(axf, ses->ses_ictx); + padlock_free_ctx(axf, ses->ses_octx); + + for (i = 0; i < klen; i++) + key[i] ^= HMAC_IPAD_VAL; + + axf->Init(ses->ses_ictx); + axf->Update(ses->ses_ictx, key, klen); + axf->Update(ses->ses_ictx, hmac_ipad_buffer, axf->blocksize - klen); + + for (i = 0; i < klen; i++) + key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); + + axf->Init(ses->ses_octx); + axf->Update(ses->ses_octx, key, klen); + axf->Update(ses->ses_octx, hmac_opad_buffer, axf->blocksize - klen); + + for (i = 0; i < klen; i++) + key[i] ^= HMAC_OPAD_VAL; +} + +/* + * Compute keyed-hash authenticator. + */ +static int +padlock_authcompute(struct padlock_session *ses, struct cryptodesc *crd, + caddr_t buf, int flags) +{ + u_char hash[HASH_MAX_LEN]; + struct auth_hash *axf; + union authctx ctx; + int error; + + axf = ses->ses_axf; + + padlock_copy_ctx(axf, ses->ses_ictx, &ctx); + error = crypto_apply(flags, buf, crd->crd_skip, crd->crd_len, + (int (*)(void *, void *, unsigned int))axf->Update, (caddr_t)&ctx); + if (error != 0) { + padlock_free_ctx(axf, &ctx); + return (error); + } + axf->Final(hash, &ctx); + + padlock_copy_ctx(axf, ses->ses_octx, &ctx); + axf->Update(&ctx, hash, axf->hashsize); + axf->Final(hash, &ctx); + + /* Inject the authentication data */ + crypto_copyback(flags, buf, crd->crd_inject, + ses->ses_mlen == 0 ? axf->hashsize : ses->ses_mlen, hash); + return (0); +} + +int +padlock_hash_setup(struct padlock_session *ses, struct cryptoini *macini) +{ + + ses->ses_mlen = macini->cri_mlen; + + /* Find software structure which describes HMAC algorithm. */ + switch (macini->cri_alg) { + case CRYPTO_NULL_HMAC: + ses->ses_axf = &auth_hash_null; + break; + case CRYPTO_MD5_HMAC: + ses->ses_axf = &auth_hash_hmac_md5; + break; + case CRYPTO_SHA1_HMAC: + if ((via_feature_xcrypt & VIA_HAS_SHA) != 0) + ses->ses_axf = &padlock_hmac_sha1; + else + ses->ses_axf = &auth_hash_hmac_sha1; + break; + case CRYPTO_RIPEMD160_HMAC: + ses->ses_axf = &auth_hash_hmac_ripemd_160; + break; + case CRYPTO_SHA2_256_HMAC: + if ((via_feature_xcrypt & VIA_HAS_SHA) != 0) + ses->ses_axf = &padlock_hmac_sha256; + else + ses->ses_axf = &auth_hash_hmac_sha2_256; + break; + case CRYPTO_SHA2_384_HMAC: + ses->ses_axf = &auth_hash_hmac_sha2_384; + break; + case CRYPTO_SHA2_512_HMAC: + ses->ses_axf = &auth_hash_hmac_sha2_512; + break; + } + + /* Allocate memory for HMAC inner and outer contexts. */ + ses->ses_ictx = malloc(ses->ses_axf->ctxsize, M_PADLOCK, + M_ZERO | M_NOWAIT); + ses->ses_octx = malloc(ses->ses_axf->ctxsize, M_PADLOCK, + M_ZERO | M_NOWAIT); + if (ses->ses_ictx == NULL || ses->ses_octx == NULL) + return (ENOMEM); + + /* Setup key if given. */ + if (macini->cri_key != NULL) { + padlock_hash_key_setup(ses, macini->cri_key, + macini->cri_klen); + } + return (0); +} + +int +padlock_hash_process(struct padlock_session *ses, struct cryptodesc *maccrd, + struct cryptop *crp) +{ + int error; + + if ((maccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) + padlock_hash_key_setup(ses, maccrd->crd_key, maccrd->crd_klen); + + error = padlock_authcompute(ses, maccrd, crp->crp_buf, crp->crp_flags); + return (error); +} + +void +padlock_hash_free(struct padlock_session *ses) +{ + + if (ses->ses_ictx != NULL) { + padlock_free_ctx(ses->ses_axf, ses->ses_ictx); + bzero(ses->ses_ictx, ses->ses_axf->ctxsize); + free(ses->ses_ictx, M_PADLOCK); + ses->ses_ictx = NULL; + } + if (ses->ses_octx != NULL) { + padlock_free_ctx(ses->ses_axf, ses->ses_octx); + bzero(ses->ses_octx, ses->ses_axf->ctxsize); + free(ses->ses_octx, M_PADLOCK); + ses->ses_octx = NULL; + } +} diff --git a/sys/dev/mii/gentbi.c b/sys/dev/mii/gentbi.c new file mode 100644 index 000000000000..06ce4589a920 --- /dev/null +++ b/sys/dev/mii/gentbi.c @@ -0,0 +1,314 @@ +/* $NetBSD: gentbi.c,v 1.15 2006/03/29 07:05:24 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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. + */ + +/* + * Copyright (c) 1997 Manuel Bouyer. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +/* + * Driver for generic ten-bit (1000BASE-SX) interfaces, built in to + * many Gigabit Ethernet chips. + * + * All we have to do here is correctly report speed and duplex. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Driver for generic unknown ten-bit interfaces(1000BASE-{LX,SX} + * fiber interfaces). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "miidevs.h" + +#include "miibus_if.h" + +static int gentbi_probe(device_t); +static int gentbi_attach(device_t); + +static device_method_t gentbi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, gentbi_probe), + DEVMETHOD(device_attach, gentbi_attach), + DEVMETHOD(device_detach, mii_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + {0, 0} +}; + +static devclass_t gentbi_devclass; + +static driver_t gentbi_driver = { + "gentbi", + gentbi_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(gentbi, miibus, gentbi_driver, gentbi_devclass, 0, 0); + +static int gentbi_service(struct mii_softc *, struct mii_data *, int); +static void gentbi_status(struct mii_softc *); + +static int +gentbi_probe(device_t dev) +{ + device_t parent; + struct mii_attach_args *ma; + int bmsr, extsr; + + parent = device_get_parent(dev); + ma = device_get_ivars(dev); + + /* + * We match as a generic TBI if: + * + * - There is no media in the BMSR. + * - EXTSR has only 1000X. + */ + bmsr = MIIBUS_READREG(parent, ma->mii_phyno, MII_BMSR); + if ((bmsr & BMSR_EXTSTAT) == 0 || (bmsr & BMSR_MEDIAMASK) != 0) + return (ENXIO); + + extsr = MIIBUS_READREG(parent, ma->mii_phyno, MII_EXTSR); + if (extsr & (EXTSR_1000TFDX|EXTSR_1000THDX)) + return (ENXIO); + + if (extsr & (EXTSR_1000XFDX|EXTSR_1000XHDX)) { + /* + * We think this is a generic TBI. Return a match + * priority higher than ukphy, but lower than what + * specific drivers will return. + */ + device_set_desc(dev, "Generic ten-bit interface"); + return (BUS_PROBE_LOW_PRIORITY); + } + + return (ENXIO); +} + +static int +gentbi_attach(device_t dev) +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + if (bootverbose) + device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", + MII_OUI(ma->mii_id1, ma->mii_id2), + MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = gentbi_service; + sc->mii_pdata = mii; + sc->mii_flags |= MIIF_NOISOLATE; + + mii->mii_instance++; + + mii_phy_reset(sc); + + /* + * Mask out all media in the BMSR. We only are really interested + * in "auto". + */ + sc->mii_capabilities = + PHY_READ(sc, MII_BMSR) & ma->mii_capmask & ~BMSR_MEDIAMASK; + if (sc->mii_capabilities & BMSR_EXTSTAT) + sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); + + device_printf(dev, " "); + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && + (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) + printf("no media present"); + else + mii_phy_add_media(sc); + printf("\n"); + + return (0); +} + +static int +gentbi_service(struct mii_softc *sc, struct mii_data *mii, int cmd) +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int reg; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + reg = PHY_READ(sc, MII_BMCR); + PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + mii_phy_setmedia(sc); + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + if (mii_phy_tick(sc) == EJUSTRETURN) + return (0); + break; + } + + /* Update the media status. */ + gentbi_status(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + return (0); +} + +static void +gentbi_status(struct mii_softc *sc) +{ + struct mii_data *mii = sc->mii_pdata; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int bmsr, bmcr, anlpar; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + + if (bmsr & BMSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(sc, MII_BMCR); + if (bmcr & BMCR_ISO) { + mii->mii_media_active |= IFM_NONE; + mii->mii_media_status = 0; + return; + } + + if (bmcr & BMCR_LOOP) + mii->mii_media_active |= IFM_LOOP; + + if (bmcr & BMCR_AUTOEN) { + /* + * The media status bits are only valid of autonegotiation + * has completed (or it's disabled). + */ + if ((bmsr & BMSR_ACOMP) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + + /* + * The media is always 1000baseSX. Check the ANLPAR to + * see if we're doing full-duplex. + */ + mii->mii_media_active |= IFM_1000_SX; + + anlpar = PHY_READ(sc, MII_ANLPAR); + if ((sc->mii_extcapabilities & EXTSR_1000XFDX) != 0 && + (anlpar & ANLPAR_X_FD) != 0) + mii->mii_media_active |= IFM_FDX; + } else + mii->mii_media_active = ife->ifm_media; +} diff --git a/sys/dev/mii/ip1000phy.c b/sys/dev/mii/ip1000phy.c new file mode 100644 index 000000000000..5b8e577dc0ed --- /dev/null +++ b/sys/dev/mii/ip1000phy.c @@ -0,0 +1,444 @@ +/*- + * Copyright (c) 2006, Pyun YongHyeon + * 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 unmodified, 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 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +/* + * Driver for the IC Plus IP1000A 10/100/1000 PHY. + */ + +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include "miidevs.h" + +#include + +#include "miibus_if.h" + +#include +#include + +static int ip1000phy_probe(device_t); +static int ip1000phy_attach(device_t); + +static device_method_t ip1000phy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ip1000phy_probe), + DEVMETHOD(device_attach, ip1000phy_attach), + DEVMETHOD(device_detach, mii_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t ip1000phy_devclass; +static driver_t ip1000phy_driver = { + "ip1000phy", + ip1000phy_methods, + sizeof (struct mii_softc) +}; + +DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0); + +static int ip1000phy_service(struct mii_softc *, struct mii_data *, int); +static void ip1000phy_status(struct mii_softc *); +static void ip1000phy_reset(struct mii_softc *); +static int ip1000phy_mii_phy_auto(struct mii_softc *); + +static const struct mii_phydesc ip1000phys[] = { + MII_PHY_DESC(ICPLUS, IP1000A), + MII_PHY_END +}; + +static int +ip1000phy_probe(device_t dev) +{ + struct mii_attach_args *ma; + const struct mii_phydesc *mpd; + + ma = device_get_ivars(dev); + mpd = mii_phy_match(ma, ip1000phys); + if (mpd != NULL) { + device_set_desc(dev, mpd->mpd_name); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +ip1000phy_attach(device_t dev) +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = ip1000phy_service; + sc->mii_pdata = mii; + sc->mii_anegticks = MII_ANEGTICKS_GIGE; + sc->mii_flags |= MIIF_NOISOLATE; + + mii->mii_instance++; + + device_printf(dev, " "); + +#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), + BMCR_ISO); + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, sc->mii_inst), + IP1000PHY_BMCR_10); + printf("10baseT, "); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, sc->mii_inst), + IP1000PHY_BMCR_10 | IP1000PHY_BMCR_FDX); + printf("10baseT-FDX, "); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, 0, sc->mii_inst), + IP1000PHY_BMCR_100); + printf("100baseTX, "); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_FDX, sc->mii_inst), + IP1000PHY_BMCR_100 | IP1000PHY_BMCR_FDX); + printf("100baseTX-FDX, "); + /* 1000baseT half-duplex, really supported? */ + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, 0, sc->mii_inst), + IP1000PHY_BMCR_1000); + printf("1000baseTX, "); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_T, IFM_FDX, sc->mii_inst), + IP1000PHY_BMCR_1000 | IP1000PHY_BMCR_FDX); + printf("1000baseTX-FDX, "); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); + printf("auto\n"); +#undef ADD + + ip1000phy_reset(sc); + + MIIBUS_MEDIAINIT(sc->mii_dev); + return(0); +} + +static int +ip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + uint32_t gig, reg, speed; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + reg = PHY_READ(sc, IP1000PHY_MII_BMCR); + PHY_WRITE(sc, IP1000PHY_MII_BMCR, + reg | IP1000PHY_BMCR_ISO); + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) { + break; + } + + ip1000phy_reset(sc); + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + (void)ip1000phy_mii_phy_auto(sc); + goto done; + break; + + case IFM_1000_T: + /* + * XXX + * Manual 1000baseT setting doesn't seem to work. + */ + speed = IP1000PHY_BMCR_1000; + break; + + case IFM_100_TX: + speed = IP1000PHY_BMCR_100; + break; + + case IFM_10_T: + speed = IP1000PHY_BMCR_10; + break; + + default: + return (EINVAL); + } + + if (((ife->ifm_media & IFM_GMASK) & IFM_FDX) != 0) { + speed |= IP1000PHY_BMCR_FDX; + gig = IP1000PHY_1000CR_1000T_FDX; + } else + gig = IP1000PHY_1000CR_1000T; + + PHY_WRITE(sc, IP1000PHY_MII_1000CR, 0); + PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); + + if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) + break; + + PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig); + PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); + + /* + * When settning the link manually, one side must + * be the master and the other the slave. However + * ifmedia doesn't give us a good way to specify + * this, so we fake it by using one of the LINK + * flags. If LINK0 is set, we program the PHY to + * be a master, otherwise it's a slave. + */ + if ((mii->mii_ifp->if_flags & IFF_LINK0)) + PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig | + IP1000PHY_1000CR_MASTER | + IP1000PHY_1000CR_MMASTER | + IP1000PHY_1000CR_MANUAL); + else + PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig | + IP1000PHY_1000CR_MASTER | + IP1000PHY_1000CR_MANUAL); + +done: + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { + sc->mii_ticks = 0; + break; + } + + /* + * check for link. + */ + reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + if (reg & BMSR_LINK) { + sc->mii_ticks = 0; + break; + } + + /* Announce link loss right after it happens */ + if (sc->mii_ticks++ == 0) + break; + + /* + * Only retry autonegotiation every mii_anegticks seconds. + */ + if (sc->mii_ticks <= sc->mii_anegticks) + return (0); + + sc->mii_ticks = 0; + ip1000phy_mii_phy_auto(sc); + break; + } + + /* Update the media status. */ + ip1000phy_status(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + return (0); +} + +static void +ip1000phy_status(struct mii_softc *sc) +{ + struct mii_data *mii = sc->mii_pdata; + uint32_t bmsr, bmcr, stat; + uint32_t ar, lpar; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) | + PHY_READ(sc, IP1000PHY_MII_BMSR); + if ((bmsr & IP1000PHY_BMSR_LINK) != 0) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR); + if ((bmcr & IP1000PHY_BMCR_LOOP) != 0) + mii->mii_media_active |= IFM_LOOP; + + if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) { + if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + } + + stat = PHY_READ(sc, STGE_PhyCtrl); + switch (PC_LinkSpeed(stat)) { + case PC_LinkSpeed_Down: + mii->mii_media_active |= IFM_NONE; + return; + case PC_LinkSpeed_10: + mii->mii_media_active |= IFM_10_T; + break; + case PC_LinkSpeed_100: + mii->mii_media_active |= IFM_100_TX; + break; + case PC_LinkSpeed_1000: + mii->mii_media_active |= IFM_1000_T; + break; + } + if ((stat & PC_PhyDuplexStatus) != 0) + mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; + + ar = PHY_READ(sc, IP1000PHY_MII_ANAR); + lpar = PHY_READ(sc, IP1000PHY_MII_ANLPAR); + + /* + * FLAG0 : Rx flow-control + * FLAG1 : Tx flow-control + */ + if ((ar & IP1000PHY_ANAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_PAUSE)) + mii->mii_media_active |= IFM_FLAG0 | IFM_FLAG1; + else if (!(ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) && + (lpar & IP1000PHY_ANLPAR_PAUSE) && (lpar & IP1000PHY_ANLPAR_APAUSE)) + mii->mii_media_active |= IFM_FLAG1; + else if ((ar & IP1000PHY_ANAR_PAUSE) && (ar & IP1000PHY_ANAR_APAUSE) && + !(lpar & IP1000PHY_ANLPAR_PAUSE) && + (lpar & IP1000PHY_ANLPAR_APAUSE)) { + mii->mii_media_active |= IFM_FLAG0; + } + + /* + * FLAG2 : local PHY resolved to MASTER + */ + if ((mii->mii_media_active & IFM_1000_T) != 0) { + stat = PHY_READ(sc, IP1000PHY_MII_1000SR); + if ((stat & IP1000PHY_1000SR_MASTER) != 0) + mii->mii_media_active |= IFM_FLAG2; + } +} + +static int +ip1000phy_mii_phy_auto(struct mii_softc *mii) +{ + uint32_t reg; + + PHY_WRITE(mii, IP1000PHY_MII_ANAR, + IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX | + IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX | + IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE); + reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX; + reg |= IP1000PHY_1000CR_MASTER; + PHY_WRITE(mii, IP1000PHY_MII_1000CR, reg); + PHY_WRITE(mii, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX | + IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG)); + + return (EJUSTRETURN); +} + +static void +ip1000phy_load_dspcode(struct mii_softc *sc) +{ + + PHY_WRITE(sc, 31, 0x0001); + PHY_WRITE(sc, 27, 0x01e0); + PHY_WRITE(sc, 31, 0x0002); + PHY_WRITE(sc, 27, 0xeb8e); + PHY_WRITE(sc, 31, 0x0000); + PHY_WRITE(sc, 30, 0x005e); + PHY_WRITE(sc, 9, 0x0700); + + DELAY(50); +} + +static void +ip1000phy_reset(struct mii_softc *sc) +{ + struct stge_softc *stge_sc; + struct mii_data *mii; + uint32_t reg; + + mii_phy_reset(sc); + + /* clear autoneg/full-duplex as we don't want it after reset */ + reg = PHY_READ(sc, IP1000PHY_MII_BMCR); + reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX); + PHY_WRITE(sc, MII_BMCR, reg); + + mii = sc->mii_pdata; + /* + * XXX There should be more general way to pass PHY specific + * data via mii interface. + */ + if (strcmp(mii->mii_ifp->if_dname, "stge") == 0) { + stge_sc = mii->mii_ifp->if_softc; + if (stge_sc->sc_rev >= 0x40 && stge_sc->sc_rev <= 0x4e) + ip1000phy_load_dspcode(sc); + } +} diff --git a/sys/dev/mii/ip1000phyreg.h b/sys/dev/mii/ip1000phyreg.h new file mode 100644 index 000000000000..26dc84071815 --- /dev/null +++ b/sys/dev/mii/ip1000phyreg.h @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 2006, Pyun YongHyeon + * 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 unmodified, 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 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MII_IP1000PHYREG_H_ +#define _DEV_MII_IP1000PHYREG_H_ + +/* + * Registers for the IC Plus IP1000A internal PHY. + */ + +/* Control register */ +#define IP1000PHY_MII_BMCR 0x00 +#define IP1000PHY_BMCR_FDX 0x0100 +#define IP1000PHY_BMCR_STARTNEG 0x0200 +#define IP1000PHY_BMCR_ISO 0x0400 +#define IP1000PHY_BMCR_PDOWN 0x0800 +#define IP1000PHY_BMCR_AUTOEN 0x1000 +#define IP1000PHY_BMCR_LOOP 0x4000 +#define IP1000PHY_BMCR_RESET 0x8000 + +#define IP1000PHY_BMCR_10 0x0000 +#define IP1000PHY_BMCR_100 0x2000 +#define IP1000PHY_BMCR_1000 0x0040 + +/* Status register */ +#define IP1000PHY_MII_BMSR 0x01 +#define IP1000PHY_BMSR_EXT 0x0001 +#define IP1000PHY_BMSR_LINK 0x0004 +#define IP1000PHY_BMSR_ANEG 0x0008 +#define IP1000PHY_BMSR_RFAULT 0x0010 +#define IP1000PHY_BMSR_ANEGCOMP 0x0020 +#define IP1000PHY_BMSR_EXTSTS 0x0100 + +#define IP1000PHY_MII_ID1 0x02 + +/* Autonegotiation advertisement register */ +#define IP1000PHY_MII_ANAR 0x04 +#define IP1000PHY_ANAR_10T 0x0020 +#define IP1000PHY_ANAR_10T_FDX 0x0040 +#define IP1000PHY_ANAR_100TX 0x0080 +#define IP1000PHY_ANAR_100TX_FDX 0x0100 +#define IP1000PHY_ANAR_100T4 0x0200 +#define IP1000PHY_ANAR_PAUSE 0x0400 +#define IP1000PHY_ANAR_APAUSE 0x0800 +#define IP1000PHY_ANAR_RFAULT 0x2000 +#define IP1000PHY_ANAR_NP 0x8000 + +/* Autonegotiation link parnet ability register */ +#define IP1000PHY_MII_ANLPAR 0x05 +#define IP1000PHY_ANLPAR_10T 0x0020 +#define IP1000PHY_ANLPAR_10T_FDX 0x0040 +#define IP1000PHY_ANLPAR_100TX 0x0080 +#define IP1000PHY_ANLPAR_100TX_FDX 0x0100 +#define IP1000PHY_ANLPAR_100T4 0x0200 +#define IP1000PHY_ANLPAR_PAUSE 0x0400 +#define IP1000PHY_ANLPAR_APAUSE 0x0800 +#define IP1000PHY_ANLPAR_RFAULT 0x2000 +#define IP1000PHY_ANLPAR_ACK 0x4000 +#define IP1000PHY_ANLPAR_NP 0x8000 + +/* Autonegotiation expansion register */ +#define IP1000PHY_MII_ANER 0x06 +#define IP1000PHY_ANER_LPNWAY 0x0001 +#define IP1000PHY_ANER_PRCVD 0x0002 +#define IP1000PHY_ANER_NEXTP 0x0004 +#define IP1000PHY_ANER_LPNEXTP 0x0008 +#define IP1000PHY_ANER_PDF 0x0100 + +/* Autonegotiation next page transmit register */ +#define IP1000PHY_MII_NEXTP 0x07 +#define IP1000PHY_NEXTP_MSGC 0x0001 +#define IP1000PHY_NEXTP_TOGGLE 0x0800 +#define IP1000PHY_NEXTP_ACK2 0x1000 +#define IP1000PHY_NEXTP_MSGP 0x2000 +#define IP1000PHY_NEXTP_NEXTP 0x8000 + +/* Autonegotiation link partner next page register */ +#define IP1000PHY_MII_NEXTPLP 0x08 +#define IP1000PHY_NEXTPLP_MSGC 0x0001 +#define IP1000PHY_NEXTPLP_TOGGLE 0x0800 +#define IP1000PHY_NEXTPLP_ACK2 0x1000 +#define IP1000PHY_NEXTPLP_MSGP 0x2000 +#define IP1000PHY_NEXTPLP_ACK 0x4000 +#define IP1000PHY_NEXTPLP_NEXTP 0x8000 + +/* 1000baseT control register */ +#define IP1000PHY_MII_1000CR 0x09 +#define IP1000PHY_1000CR_1000T 0x0100 +#define IP1000PHY_1000CR_1000T_FDX 0x0200 +#define IP1000PHY_1000CR_MASTER 0x0400 +#define IP1000PHY_1000CR_MMASTER 0x0800 +#define IP1000PHY_1000CR_MANUAL 0x1000 +#define IP1000PHY_1000CR_TMNORMAL 0x0000 +#define IP1000PHY_1000CR_TM1 0x2000 +#define IP1000PHY_1000CR_TM2 0x4000 +#define IP1000PHY_1000CR_TM3 0x6000 +#define IP1000PHY_1000CR_TM4 0x8000 + +/* 1000baseT status register */ +#define IP1000PHY_MII_1000SR 0x0A +#define IP1000PHY_1000SR_LP 0x0400 +#define IP1000PHY_1000SR_LP_FDX 0x0800 +#define IP1000PHY_1000SR_RXSTAT 0x1000 +#define IP1000PHY_1000SR_LRXSTAT 0x2000 +#define IP1000PHY_1000SR_MASTER 0x4000 +#define IP1000PHY_1000SR_MASTERF 0x8000 + +/* Extended status register */ +#define IP1000PHY_MII_EXTSTS 0x0F +#define IP1000PHY_EXTSTS_1000T 0x1000 +#define IP1000PHY_EXTSTS_1000T_FDX 0x2000 +#define IP1000PHY_EXTSTS_1000X 0x4000 +#define IP1000PHY_EXTSTS_1000X_FDX 0x8000 + +#endif /* _DEV_MII_IP1000PHYREG_H_ */ diff --git a/sys/dev/stge/if_stge.c b/sys/dev/stge/if_stge.c new file mode 100644 index 000000000000..f346ca37acd0 --- /dev/null +++ b/sys/dev/stge/if_stge.c @@ -0,0 +1,2698 @@ +/* $NetBSD: if_stge.c,v 1.32 2005/12/11 12:22:49 christos Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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. + */ + +/* + * Device driver for the Sundance Tech. TC9021 10/100/1000 + * Ethernet controller. + */ + +#include +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define STGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +MODULE_DEPEND(stge, pci, 1, 1, 1); +MODULE_DEPEND(stge, ether, 1, 1, 1); +MODULE_DEPEND(stge, miibus, 1, 1, 1); + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +/* + * Devices supported by this driver. + */ +static struct stge_product { + uint16_t stge_vendorid; + uint16_t stge_deviceid; + const char *stge_name; +} stge_products[] = { + { VENDOR_SUNDANCETI, DEVICEID_SUNDANCETI_ST1023, + "Sundance ST-1023 Gigabit Ethernet" }, + + { VENDOR_SUNDANCETI, DEVICEID_SUNDANCETI_ST2021, + "Sundance ST-2021 Gigabit Ethernet" }, + + { VENDOR_TAMARACK, DEVICEID_TAMARACK_TC9021, + "Tamarack TC9021 Gigabit Ethernet" }, + + { VENDOR_TAMARACK, DEVICEID_TAMARACK_TC9021_ALT, + "Tamarack TC9021 Gigabit Ethernet" }, + + /* + * The Sundance sample boards use the Sundance vendor ID, + * but the Tamarack product ID. + */ + { VENDOR_SUNDANCETI, DEVICEID_TAMARACK_TC9021, + "Sundance TC9021 Gigabit Ethernet" }, + + { VENDOR_SUNDANCETI, DEVICEID_TAMARACK_TC9021_ALT, + "Sundance TC9021 Gigabit Ethernet" }, + + { VENDOR_DLINK, DEVICEID_DLINK_DL4000, + "D-Link DL-4000 Gigabit Ethernet" }, + + { VENDOR_ANTARES, DEVICEID_ANTARES_TC9021, + "Antares Gigabit Ethernet" } +}; + +static int stge_probe(device_t); +static int stge_attach(device_t); +static int stge_detach(device_t); +static void stge_shutdown(device_t); +static int stge_suspend(device_t); +static int stge_resume(device_t); + +static int stge_encap(struct stge_softc *, struct mbuf **); +static void stge_start(struct ifnet *); +static void stge_start_locked(struct ifnet *); +static void stge_watchdog(struct ifnet *); +static int stge_ioctl(struct ifnet *, u_long, caddr_t); +static void stge_init(void *); +static void stge_init_locked(struct stge_softc *); +static void stge_vlan_setup(struct stge_softc *); +static void stge_stop(struct stge_softc *); +static void stge_start_tx(struct stge_softc *); +static void stge_start_rx(struct stge_softc *); +static void stge_stop_tx(struct stge_softc *); +static void stge_stop_rx(struct stge_softc *); + +static void stge_reset(struct stge_softc *, uint32_t); +static int stge_eeprom_wait(struct stge_softc *); +static void stge_read_eeprom(struct stge_softc *, int, uint16_t *); +static void stge_tick(void *); +static void stge_stats_update(struct stge_softc *); +static void stge_set_filter(struct stge_softc *); +static void stge_set_multi(struct stge_softc *); + +static void stge_link_task(void *, int); +static void stge_intr(void *); +static __inline int stge_tx_error(struct stge_softc *); +static void stge_txeof(struct stge_softc *); +static void stge_rxeof(struct stge_softc *); +static __inline void stge_discard_rxbuf(struct stge_softc *, int); +static int stge_newbuf(struct stge_softc *, int); +#ifndef __NO_STRICT_ALIGNMENT +static __inline struct mbuf *stge_fixup_rx(struct stge_softc *, struct mbuf *); +#endif + +static void stge_mii_sync(struct stge_softc *); +static void stge_mii_send(struct stge_softc *, uint32_t, int); +static int stge_mii_readreg(struct stge_softc *, struct stge_mii_frame *); +static int stge_mii_writereg(struct stge_softc *, struct stge_mii_frame *); +static int stge_miibus_readreg(device_t, int, int); +static int stge_miibus_writereg(device_t, int, int, int); +static void stge_miibus_statchg(device_t); +static int stge_mediachange(struct ifnet *); +static void stge_mediastatus(struct ifnet *, struct ifmediareq *); + +static void stge_dmamap_cb(void *, bus_dma_segment_t *, int, int); +static int stge_dma_alloc(struct stge_softc *); +static void stge_dma_free(struct stge_softc *); +static void stge_dma_wait(struct stge_softc *); +static void stge_init_tx_ring(struct stge_softc *); +static int stge_init_rx_ring(struct stge_softc *); +#ifdef DEVICE_POLLING +static void stge_poll(struct ifnet *, enum poll_cmd, int); +#endif + +static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); +static int sysctl_hw_stge_rxint_nframe(SYSCTL_HANDLER_ARGS); +static int sysctl_hw_stge_rxint_dmawait(SYSCTL_HANDLER_ARGS); + +static device_method_t stge_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, stge_probe), + DEVMETHOD(device_attach, stge_attach), + DEVMETHOD(device_detach, stge_detach), + DEVMETHOD(device_shutdown, stge_shutdown), + DEVMETHOD(device_suspend, stge_suspend), + DEVMETHOD(device_resume, stge_resume), + + /* MII interface */ + DEVMETHOD(miibus_readreg, stge_miibus_readreg), + DEVMETHOD(miibus_writereg, stge_miibus_writereg), + DEVMETHOD(miibus_statchg, stge_miibus_statchg), + + { 0, 0 } + +}; + +static driver_t stge_driver = { + "stge", + stge_methods, + sizeof(struct stge_softc) +}; + +static devclass_t stge_devclass; + +DRIVER_MODULE(stge, pci, stge_driver, stge_devclass, 0, 0); +DRIVER_MODULE(miibus, stge, miibus_driver, miibus_devclass, 0, 0); + +static struct resource_spec stge_res_spec_io[] = { + { SYS_RES_IOPORT, PCIR_BAR(0), RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + +static struct resource_spec stge_res_spec_mem[] = { + { SYS_RES_MEMORY, PCIR_BAR(1), RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + +#define MII_SET(x) \ + CSR_WRITE_1(sc, STGE_PhyCtrl, CSR_READ_1(sc, STGE_PhyCtrl) | (x)) +#define MII_CLR(x) \ + CSR_WRITE_1(sc, STGE_PhyCtrl, CSR_READ_1(sc, STGE_PhyCtrl) & ~(x)) + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void +stge_mii_sync(struct stge_softc *sc) +{ + int i; + + MII_SET(PC_MgmtDir | PC_MgmtData); + + for (i = 0; i < 32; i++) { + MII_SET(PC_MgmtClk); + DELAY(1); + MII_CLR(PC_MgmtClk); + DELAY(1); + } +} + +/* + * Clock a series of bits through the MII. + */ +static void +stge_mii_send(struct stge_softc *sc, uint32_t bits, int cnt) +{ + int i; + + MII_CLR(PC_MgmtClk); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) + MII_SET(PC_MgmtData); + else + MII_CLR(PC_MgmtData); + DELAY(1); + MII_CLR(PC_MgmtClk); + DELAY(1); + MII_SET(PC_MgmtClk); + } +} + +/* + * Read an PHY register through the MII. + */ +static int +stge_mii_readreg(struct stge_softc *sc, struct stge_mii_frame *frame) +{ + int i, ack; + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = STGE_MII_STARTDELIM; + frame->mii_opcode = STGE_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_1(sc, STGE_PhyCtrl, 0 | sc->sc_PhyCtrl); + /* + * Turn on data xmit. + */ + MII_SET(PC_MgmtDir); + + stge_mii_sync(sc); + + /* + * Send command/address info. + */ + stge_mii_send(sc, frame->mii_stdelim, 2); + stge_mii_send(sc, frame->mii_opcode, 2); + stge_mii_send(sc, frame->mii_phyaddr, 5); + stge_mii_send(sc, frame->mii_regaddr, 5); + + /* Turn off xmit. */ + MII_CLR(PC_MgmtDir); + + /* Idle bit */ + MII_CLR((PC_MgmtClk | PC_MgmtData)); + DELAY(1); + MII_SET(PC_MgmtClk); + DELAY(1); + + /* Check for ack */ + MII_CLR(PC_MgmtClk); + DELAY(1); + ack = CSR_READ_1(sc, STGE_PhyCtrl) & PC_MgmtData; + MII_SET(PC_MgmtClk); + DELAY(1); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + MII_CLR(PC_MgmtClk); + DELAY(1); + MII_SET(PC_MgmtClk); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + MII_CLR(PC_MgmtClk); + DELAY(1); + if (!ack) { + if (CSR_READ_1(sc, STGE_PhyCtrl) & PC_MgmtData) + frame->mii_data |= i; + DELAY(1); + } + MII_SET(PC_MgmtClk); + DELAY(1); + } + +fail: + MII_CLR(PC_MgmtClk); + DELAY(1); + MII_SET(PC_MgmtClk); + DELAY(1); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int +stge_mii_writereg(struct stge_softc *sc, struct stge_mii_frame *frame) +{ + + /* + * Set up frame for TX. + */ + frame->mii_stdelim = STGE_MII_STARTDELIM; + frame->mii_opcode = STGE_MII_WRITEOP; + frame->mii_turnaround = STGE_MII_TURNAROUND; + + /* + * Turn on data output. + */ + MII_SET(PC_MgmtDir); + + stge_mii_sync(sc); + + stge_mii_send(sc, frame->mii_stdelim, 2); + stge_mii_send(sc, frame->mii_opcode, 2); + stge_mii_send(sc, frame->mii_phyaddr, 5); + stge_mii_send(sc, frame->mii_regaddr, 5); + stge_mii_send(sc, frame->mii_turnaround, 2); + stge_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + MII_SET(PC_MgmtClk); + DELAY(1); + MII_CLR(PC_MgmtClk); + DELAY(1); + + /* + * Turn off xmit. + */ + MII_CLR(PC_MgmtDir); + + return(0); +} + +/* + * sc_miibus_readreg: [mii interface function] + * + * Read a PHY register on the MII of the TC9021. + */ +static int +stge_miibus_readreg(device_t dev, int phy, int reg) +{ + struct stge_softc *sc; + struct stge_mii_frame frame; + int error; + + sc = device_get_softc(dev); + + if (reg == STGE_PhyCtrl) { + /* XXX allow ip1000phy read STGE_PhyCtrl register. */ + STGE_MII_LOCK(sc); + error = CSR_READ_1(sc, STGE_PhyCtrl); + STGE_MII_UNLOCK(sc); + return (error); + } + bzero(&frame, sizeof(frame)); + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + + STGE_MII_LOCK(sc); + error = stge_mii_readreg(sc, &frame); + STGE_MII_UNLOCK(sc); + + if (error != 0) { + /* Don't show errors for PHY probe request */ + if (reg != 1) + device_printf(sc->sc_dev, "phy read fail\n"); + return (0); + } + return (frame.mii_data); +} + +/* + * stge_miibus_writereg: [mii interface function] + * + * Write a PHY register on the MII of the TC9021. + */ +static int +stge_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct stge_softc *sc; + struct stge_mii_frame frame; + int error; + + sc = device_get_softc(dev); + + bzero(&frame, sizeof(frame)); + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = val; + + STGE_MII_LOCK(sc); + error = stge_mii_writereg(sc, &frame); + STGE_MII_UNLOCK(sc); + + if (error != 0) + device_printf(sc->sc_dev, "phy write fail\n"); + return (0); +} + +/* + * stge_miibus_statchg: [mii interface function] + * + * Callback from MII layer when media changes. + */ +static void +stge_miibus_statchg(device_t dev) +{ + struct stge_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->sc_miibus); + + STGE_MII_LOCK(sc); + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE) { + STGE_MII_UNLOCK(sc); + return; + } + + sc->sc_MACCtrl = 0; + if (((mii->mii_media_active & IFM_GMASK) & IFM_FDX) != 0) + sc->sc_MACCtrl |= MC_DuplexSelect; + if (((mii->mii_media_active & IFM_GMASK) & IFM_FLAG0) != 0) + sc->sc_MACCtrl |= MC_RxFlowControlEnable; + if (((mii->mii_media_active & IFM_GMASK) & IFM_FLAG1) != 0) + sc->sc_MACCtrl |= MC_TxFlowControlEnable; + /* + * We can't access STGE_MACCtrl register in this context due to + * the races between MII layer and driver which accesses this + * register to program MAC. In order to solve the race, we defer + * STGE_MACCtrl programming until we know we are out of MII. + */ + taskqueue_enqueue(taskqueue_swi, &sc->sc_link_task); + STGE_MII_UNLOCK(sc); +} + +/* + * stge_mediastatus: [ifmedia interface function] + * + * Get the current interface media status. + */ +static void +stge_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct stge_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->sc_miibus); + + mii_pollstat(mii); + ifmr->ifm_status = mii->mii_media_status; + ifmr->ifm_active = mii->mii_media_active; +} + +/* + * stge_mediachange: [ifmedia interface function] + * + * Set hardware to newly-selected media. + */ +static int +stge_mediachange(struct ifnet *ifp) +{ + struct stge_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->sc_miibus); + mii_mediachg(mii); + + return (0); +} + +static int +stge_eeprom_wait(struct stge_softc *sc) +{ + int i; + + for (i = 0; i < STGE_TIMEOUT; i++) { + DELAY(1000); + if ((CSR_READ_2(sc, STGE_EepromCtrl) & EC_EepromBusy) == 0) + return (0); + } + return (1); +} + +/* + * stge_read_eeprom: + * + * Read data from the serial EEPROM. + */ +static void +stge_read_eeprom(struct stge_softc *sc, int offset, uint16_t *data) +{ + + if (stge_eeprom_wait(sc)) + device_printf(sc->sc_dev, "EEPROM failed to come ready\n"); + + CSR_WRITE_2(sc, STGE_EepromCtrl, + EC_EepromAddress(offset) | EC_EepromOpcode(EC_OP_RR)); + if (stge_eeprom_wait(sc)) + device_printf(sc->sc_dev, "EEPROM read timed out\n"); + *data = CSR_READ_2(sc, STGE_EepromData); +} + + +static int +stge_probe(device_t dev) +{ + struct stge_product *sp; + int i; + uint16_t vendor, devid; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + sp = stge_products; + for (i = 0; i < sizeof(stge_products)/sizeof(stge_products[0]); + i++, sp++) { + if (vendor == sp->stge_vendorid && + devid == sp->stge_deviceid) { + device_set_desc(dev, sp->stge_name); + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +} + +static int +stge_attach(device_t dev) +{ + struct stge_softc *sc; + struct ifnet *ifp; + uint8_t enaddr[ETHER_ADDR_LEN]; + int error, i; + uint16_t cmd; + uint32_t val; + + error = 0; + sc = device_get_softc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + mtx_init(&sc->sc_mii_mtx, "stge_mii_mutex", NULL, MTX_DEF); + callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_link_task, 0, stge_link_task, sc); + + /* + * Map the device. + */ + pci_enable_busmaster(dev); + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + val = pci_read_config(dev, PCIR_BAR(1), 4); + if ((val & 0x01) != 0) + sc->sc_spec = stge_res_spec_mem; + else { + val = pci_read_config(dev, PCIR_BAR(0), 4); + if ((val & 0x01) == 0) { + device_printf(sc->sc_dev, "couldn't locate IO BAR\n"); + error = ENXIO; + goto fail; + } + sc->sc_spec = stge_res_spec_io; + } + error = bus_alloc_resources(dev, sc->sc_spec, sc->sc_res); + if (error != 0) { + device_printf(dev, "couldn't allocate %s resources\n", + sc->sc_spec == stge_res_spec_mem ? "memory" : "I/O"); + goto fail; + } + sc->sc_rev = pci_get_revid(dev); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rxint_nframe", CTLTYPE_INT|CTLFLAG_RW, &sc->sc_rxint_nframe, 0, + sysctl_hw_stge_rxint_nframe, "I", "stge rx interrupt nframe"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rxint_dmawait", CTLTYPE_INT|CTLFLAG_RW, &sc->sc_rxint_dmawait, 0, + sysctl_hw_stge_rxint_dmawait, "I", "stge rx interrupt dmawait"); + + /* Pull in device tunables. */ + sc->sc_rxint_nframe = STGE_RXINT_NFRAME_DEFAULT; + error = resource_int_value(device_get_name(dev), device_get_unit(dev), + "rxint_nframe", &sc->sc_rxint_nframe); + if (error == 0) { + if (sc->sc_rxint_nframe < STGE_RXINT_NFRAME_MIN || + sc->sc_rxint_nframe > STGE_RXINT_NFRAME_MAX) { + device_printf(dev, "rxint_nframe value out of range; " + "using default: %d\n", STGE_RXINT_NFRAME_DEFAULT); + sc->sc_rxint_nframe = STGE_RXINT_NFRAME_DEFAULT; + } + } + + sc->sc_rxint_dmawait = STGE_RXINT_DMAWAIT_DEFAULT; + error = resource_int_value(device_get_name(dev), device_get_unit(dev), + "rxint_dmawait", &sc->sc_rxint_dmawait); + if (error == 0) { + if (sc->sc_rxint_dmawait < STGE_RXINT_DMAWAIT_MIN || + sc->sc_rxint_dmawait > STGE_RXINT_DMAWAIT_MAX) { + device_printf(dev, "rxint_dmawait value out of range; " + "using default: %d\n", STGE_RXINT_DMAWAIT_DEFAULT); + sc->sc_rxint_dmawait = STGE_RXINT_DMAWAIT_DEFAULT; + } + } + + if ((error = stge_dma_alloc(sc) != 0)) + goto fail; + + /* + * Determine if we're copper or fiber. It affects how we + * reset the card. + */ + if (CSR_READ_4(sc, STGE_AsicCtrl) & AC_PhyMedia) + sc->sc_usefiber = 1; + else + sc->sc_usefiber = 0; + + /* Load LED configuration from EEPROM. */ + stge_read_eeprom(sc, STGE_EEPROM_LEDMode, &sc->sc_led); + + /* + * Reset the chip to a known state. + */ + STGE_LOCK(sc); + stge_reset(sc, STGE_RESET_FULL); + STGE_UNLOCK(sc); + + /* + * Reading the station address from the EEPROM doesn't seem + * to work, at least on my sample boards. Instead, since + * the reset sequence does AutoInit, read it from the station + * address registers. For Sundance 1023 you can only read it + * from EEPROM. + */ + if (pci_get_device(dev) != DEVICEID_SUNDANCETI_ST1023) { + uint16_t v; + + v = CSR_READ_2(sc, STGE_StationAddress0); + enaddr[0] = v & 0xff; + enaddr[1] = v >> 8; + v = CSR_READ_2(sc, STGE_StationAddress1); + enaddr[2] = v & 0xff; + enaddr[3] = v >> 8; + v = CSR_READ_2(sc, STGE_StationAddress2); + enaddr[4] = v & 0xff; + enaddr[5] = v >> 8; + sc->sc_stge1023 = 0; + } else { + uint16_t myaddr[ETHER_ADDR_LEN / 2]; + for (i = 0; i sc_stge1023 = 1; + } + + ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(sc->sc_dev, "failed to if_alloc()\n"); + error = ENXIO; + goto fail; + } + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = stge_ioctl; + ifp->if_start = stge_start; + ifp->if_watchdog = stge_watchdog; + ifp->if_init = stge_init; + ifp->if_mtu = ETHERMTU; + ifp->if_snd.ifq_drv_maxlen = STGE_TX_RING_CNT - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); + IFQ_SET_READY(&ifp->if_snd); + /* Revision B3 and earlier chips have checksum bug. */ + if (sc->sc_rev >= 0x0c) { + ifp->if_hwassist = STGE_CSUM_FEATURES; + ifp->if_capabilities = IFCAP_HWCSUM; + } else { + ifp->if_hwassist = 0; + ifp->if_capabilities = 0; + } + ifp->if_capenable = ifp->if_capabilities; + + /* + * Read some important bits from the PhyCtrl register. + */ + sc->sc_PhyCtrl = CSR_READ_1(sc, STGE_PhyCtrl) & + (PC_PhyDuplexPolarity | PC_PhyLnkPolarity); + + /* Set up MII bus. */ + if ((error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, stge_mediachange, + stge_mediastatus)) != 0) { + device_printf(sc->sc_dev, "no PHY found!\n"); + goto fail; + } + + ether_ifattach(ifp, enaddr); + + /* VLAN capability setup */ + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; + if (sc->sc_rev >= 0x0c) + ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; + ifp->if_capenable = ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif + /* + * Tell the upper layer(s) we support long frames. + * Must appear after the call to ether_ifattach() because + * ether_ifattach() sets ifi_hdrlen to the default value. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + + /* + * The manual recommends disabling early transmit, so we + * do. It's disabled anyway, if using IP checksumming, + * since the entire packet must be in the FIFO in order + * for the chip to perform the checksum. + */ + sc->sc_txthresh = 0x0fff; + + /* + * Disable MWI if the PCI layer tells us to. + */ + sc->sc_DMACtrl = 0; + if ((cmd & PCIM_CMD_MWRICEN) == 0) + sc->sc_DMACtrl |= DMAC_MWIDisable; + + /* + * Hookup IRQ + */ + error = bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_NET | INTR_MPSAFE, + stge_intr, sc, &sc->sc_ih); + if (error != 0) { + ether_ifdetach(ifp); + device_printf(sc->sc_dev, "couldn't set up IRQ\n"); + sc->sc_ifp = NULL; + goto fail; + } + +fail: + if (error != 0) + stge_detach(dev); + + return (error); +} + +static int +stge_detach(device_t dev) +{ + struct stge_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + + ifp = sc->sc_ifp; +#ifdef DEVICE_POLLING + if (ifp && ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + if (device_is_attached(dev)) { + STGE_LOCK(sc); + /* XXX */ + sc->sc_detach = 1; + stge_stop(sc); + STGE_UNLOCK(sc); + callout_drain(&sc->sc_tick_ch); + taskqueue_drain(taskqueue_swi, &sc->sc_link_task); + ether_ifdetach(ifp); + } + + if (sc->sc_miibus != NULL) { + device_delete_child(dev, sc->sc_miibus); + sc->sc_miibus = NULL; + } + bus_generic_detach(dev); + stge_dma_free(sc); + + if (ifp != NULL) { + if_free(ifp); + sc->sc_ifp = NULL; + } + + if (sc->sc_ih) { + bus_teardown_intr(dev, sc->sc_res[1], sc->sc_ih); + sc->sc_ih = NULL; + } + bus_release_resources(dev, sc->sc_spec, sc->sc_res); + + mtx_destroy(&sc->sc_mii_mtx); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +struct stge_dmamap_arg { + bus_addr_t stge_busaddr; +}; + +static void +stge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct stge_dmamap_arg *ctx; + + if (error != 0) + return; + + ctx = (struct stge_dmamap_arg *)arg; + ctx->stge_busaddr = segs[0].ds_addr; +} + +static int +stge_dma_alloc(struct stge_softc *sc) +{ + struct stge_dmamap_arg ctx; + struct stge_txdesc *txd; + struct stge_rxdesc *rxd; + int error, i; + + /* create parent tag. */ + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* algnmnt, boundary */ + STGE_DMA_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_cdata.stge_parent_tag); + if (error != 0) { + device_printf(sc->sc_dev, "failed to create parent DMA tag\n"); + goto fail; + } + /* create tag for Tx ring. */ + error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */ + STGE_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + STGE_TX_RING_SZ, /* maxsize */ + 1, /* nsegments */ + STGE_TX_RING_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_cdata.stge_tx_ring_tag); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate Tx ring DMA tag\n"); + goto fail; + } + + /* create tag for Rx ring. */ + error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */ + STGE_RING_ALIGN, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + STGE_RX_RING_SZ, /* maxsize */ + 1, /* nsegments */ + STGE_RX_RING_SZ, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_cdata.stge_rx_ring_tag); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate Rx ring DMA tag\n"); + goto fail; + } + + /* create tag for Tx buffers. */ + error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * STGE_MAXTXSEGS, /* maxsize */ + STGE_MAXTXSEGS, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_cdata.stge_tx_tag); + if (error != 0) { + device_printf(sc->sc_dev, "failed to allocate Tx DMA tag\n"); + goto fail; + } + + /* create tag for Rx buffers. */ + error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_cdata.stge_rx_tag); + if (error != 0) { + device_printf(sc->sc_dev, "failed to allocate Rx DMA tag\n"); + goto fail; + } + + /* allocate DMA'able memory and load the DMA map for Tx ring. */ + error = bus_dmamem_alloc(sc->sc_cdata.stge_tx_ring_tag, + (void **)&sc->sc_rdata.stge_tx_ring, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc->sc_cdata.stge_tx_ring_map); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate DMA'able memory for Tx ring\n"); + goto fail; + } + + ctx.stge_busaddr = 0; + error = bus_dmamap_load(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map, sc->sc_rdata.stge_tx_ring, + STGE_TX_RING_SZ, stge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.stge_busaddr == 0) { + device_printf(sc->sc_dev, + "failed to load DMA'able memory for Tx ring\n"); + goto fail; + } + sc->sc_rdata.stge_tx_ring_paddr = ctx.stge_busaddr; + + /* allocate DMA'able memory and load the DMA map for Rx ring. */ + error = bus_dmamem_alloc(sc->sc_cdata.stge_rx_ring_tag, + (void **)&sc->sc_rdata.stge_rx_ring, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc->sc_cdata.stge_rx_ring_map); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to allocate DMA'able memory for Rx ring\n"); + goto fail; + } + + ctx.stge_busaddr = 0; + error = bus_dmamap_load(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_cdata.stge_rx_ring_map, sc->sc_rdata.stge_rx_ring, + STGE_RX_RING_SZ, stge_dmamap_cb, &ctx, BUS_DMA_NOWAIT); + if (error != 0 || ctx.stge_busaddr == 0) { + device_printf(sc->sc_dev, + "failed to load DMA'able memory for Rx ring\n"); + goto fail; + } + sc->sc_rdata.stge_rx_ring_paddr = ctx.stge_busaddr; + + /* create DMA maps for Tx buffers. */ + for (i = 0; i < STGE_TX_RING_CNT; i++) { + txd = &sc->sc_cdata.stge_txdesc[i]; + txd->tx_m = NULL; + txd->tx_dmamap = 0; + error = bus_dmamap_create(sc->sc_cdata.stge_tx_tag, 0, + &txd->tx_dmamap); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to create Tx dmamap\n"); + goto fail; + } + } + /* create DMA maps for Rx buffers. */ + if ((error = bus_dmamap_create(sc->sc_cdata.stge_rx_tag, 0, + &sc->sc_cdata.stge_rx_sparemap)) != 0) { + device_printf(sc->sc_dev, "failed to create spare Rx dmamap\n"); + goto fail; + } + for (i = 0; i < STGE_RX_RING_CNT; i++) { + rxd = &sc->sc_cdata.stge_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_dmamap = 0; + error = bus_dmamap_create(sc->sc_cdata.stge_rx_tag, 0, + &rxd->rx_dmamap); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to create Rx dmamap\n"); + goto fail; + } + } + +fail: + return (error); +} + +static void +stge_dma_free(struct stge_softc *sc) +{ + struct stge_txdesc *txd; + struct stge_rxdesc *rxd; + int i; + + /* Tx ring */ + if (sc->sc_cdata.stge_tx_ring_tag) { + if (sc->sc_cdata.stge_tx_ring_map) + bus_dmamap_unload(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map); + if (sc->sc_cdata.stge_tx_ring_map && + sc->sc_rdata.stge_tx_ring) + bus_dmamem_free(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_rdata.stge_tx_ring, + sc->sc_cdata.stge_tx_ring_map); + sc->sc_rdata.stge_tx_ring = NULL; + sc->sc_cdata.stge_tx_ring_map = 0; + bus_dma_tag_destroy(sc->sc_cdata.stge_tx_ring_tag); + sc->sc_cdata.stge_tx_ring_tag = NULL; + } + /* Rx ring */ + if (sc->sc_cdata.stge_rx_ring_tag) { + if (sc->sc_cdata.stge_rx_ring_map) + bus_dmamap_unload(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_cdata.stge_rx_ring_map); + if (sc->sc_cdata.stge_rx_ring_map && + sc->sc_rdata.stge_rx_ring) + bus_dmamem_free(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_rdata.stge_rx_ring, + sc->sc_cdata.stge_rx_ring_map); + sc->sc_rdata.stge_rx_ring = NULL; + sc->sc_cdata.stge_rx_ring_map = 0; + bus_dma_tag_destroy(sc->sc_cdata.stge_rx_ring_tag); + sc->sc_cdata.stge_rx_ring_tag = NULL; + } + /* Tx buffers */ + if (sc->sc_cdata.stge_tx_tag) { + for (i = 0; i < STGE_TX_RING_CNT; i++) { + txd = &sc->sc_cdata.stge_txdesc[i]; + if (txd->tx_dmamap) { + bus_dmamap_destroy(sc->sc_cdata.stge_tx_tag, + txd->tx_dmamap); + txd->tx_dmamap = 0; + } + } + bus_dma_tag_destroy(sc->sc_cdata.stge_tx_tag); + sc->sc_cdata.stge_tx_tag = NULL; + } + /* Rx buffers */ + if (sc->sc_cdata.stge_rx_tag) { + for (i = 0; i < STGE_RX_RING_CNT; i++) { + rxd = &sc->sc_cdata.stge_rxdesc[i]; + if (rxd->rx_dmamap) { + bus_dmamap_destroy(sc->sc_cdata.stge_rx_tag, + rxd->rx_dmamap); + rxd->rx_dmamap = 0; + } + } + if (sc->sc_cdata.stge_rx_sparemap) { + bus_dmamap_destroy(sc->sc_cdata.stge_rx_tag, + sc->sc_cdata.stge_rx_sparemap); + sc->sc_cdata.stge_rx_sparemap = 0; + } + bus_dma_tag_destroy(sc->sc_cdata.stge_rx_tag); + sc->sc_cdata.stge_rx_tag = NULL; + } + + if (sc->sc_cdata.stge_parent_tag) { + bus_dma_tag_destroy(sc->sc_cdata.stge_parent_tag); + sc->sc_cdata.stge_parent_tag = NULL; + } +} + +/* + * stge_shutdown: + * + * Make sure the interface is stopped at reboot time. + */ +static void +stge_shutdown(device_t dev) +{ + struct stge_softc *sc; + + sc = device_get_softc(dev); + + STGE_LOCK(sc); + stge_stop(sc); + STGE_UNLOCK(sc); +} + +static int +stge_suspend(device_t dev) +{ + struct stge_softc *sc; + + sc = device_get_softc(dev); + + STGE_LOCK(sc); + stge_stop(sc); + sc->sc_suspended = 1; + STGE_UNLOCK(sc); + + return (0); +} + +static int +stge_resume(device_t dev) +{ + struct stge_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + + STGE_LOCK(sc); + ifp = sc->sc_ifp; + if (ifp->if_flags & IFF_UP) + stge_init_locked(sc); + + sc->sc_suspended = 0; + STGE_UNLOCK(sc); + + return (0); +} + +static void +stge_dma_wait(struct stge_softc *sc) +{ + int i; + + for (i = 0; i < STGE_TIMEOUT; i++) { + DELAY(2); + if ((CSR_READ_4(sc, STGE_DMACtrl) & DMAC_TxDMAInProg) == 0) + break; + } + + if (i == STGE_TIMEOUT) + device_printf(sc->sc_dev, "DMA wait timed out\n"); +} + +static int +stge_encap(struct stge_softc *sc, struct mbuf **m_head) +{ + struct stge_txdesc *txd; + struct stge_tfd *tfd; + struct mbuf *m, *n; + struct m_tag *mtag; + bus_dma_segment_t txsegs[STGE_MAXTXSEGS]; + int error, i, nsegs, si; + uint64_t csum_flags, tfc; + + STGE_LOCK_ASSERT(sc); + + if ((txd = STAILQ_FIRST(&sc->sc_cdata.stge_txfreeq)) == NULL) + return (ENOBUFS); + + m = *m_head; + error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_tx_tag, + txd->tx_dmamap, m, txsegs, &nsegs, 0); + if (error == EFBIG) { + n = m_defrag(m, M_DONTWAIT); + if (n == NULL) { + m_freem(m); + m = NULL; + return (ENOMEM); + } + m = n; + error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_tx_tag, + txd->tx_dmamap, m, txsegs, &nsegs, 0); + if (error != 0) { + m_freem(m); + m = NULL; + return (error); + } + } else if (error != 0) + return (error); + if (nsegs == 0) { + m_freem(m); + m = NULL; + return (EIO); + } + + csum_flags = 0; + if ((m->m_pkthdr.csum_flags & STGE_CSUM_FEATURES) != 0) { + if (m->m_pkthdr.csum_flags & CSUM_IP) + csum_flags |= TFD_IPChecksumEnable; + if (m->m_pkthdr.csum_flags & CSUM_TCP) + csum_flags |= TFD_TCPChecksumEnable; + else if (m->m_pkthdr.csum_flags & CSUM_UDP) + csum_flags |= TFD_UDPChecksumEnable; + } + + si = sc->sc_cdata.stge_tx_prod; + tfd = &sc->sc_rdata.stge_tx_ring[si]; + for (i = 0; i < nsegs; i++) + tfd->tfd_frags[i].frag_word0 = + htole64(FRAG_ADDR(txsegs[i].ds_addr) | + FRAG_LEN(txsegs[i].ds_len)); + sc->sc_cdata.stge_tx_cnt++; + + tfc = TFD_FrameId(si) | TFD_WordAlign(TFD_WordAlign_disable) | + TFD_FragCount(nsegs) | csum_flags; + if (sc->sc_cdata.stge_tx_cnt >= STGE_TX_HIWAT) + tfc |= TFD_TxDMAIndicate; + + /* Update producer index. */ + sc->sc_cdata.stge_tx_prod = (si + 1) % STGE_TX_RING_CNT; + + /* Check if we have a VLAN tag to insert. */ + mtag = VLAN_OUTPUT_TAG(sc->sc_ifp, m); + if (mtag != NULL) + tfc |= TFD_VLANTagInsert | TFD_VID(VLAN_TAG_VALUE(mtag)); + tfd->tfd_control = htole64(tfc); + + /* Update Tx Queue. */ + STAILQ_REMOVE_HEAD(&sc->sc_cdata.stge_txfreeq, tx_q); + STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txbusyq, txd, tx_q); + txd->tx_m = m; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * stge_start: [ifnet interface function] + * + * Start packet transmission on the interface. + */ +static void +stge_start(struct ifnet *ifp) +{ + struct stge_softc *sc; + + sc = ifp->if_softc; + STGE_LOCK(sc); + stge_start_locked(ifp); + STGE_UNLOCK(sc); +} + +static void +stge_start_locked(struct ifnet *ifp) +{ + struct stge_softc *sc; + struct mbuf *m_head; + int enq; + + sc = ifp->if_softc; + + STGE_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { + if (sc->sc_cdata.stge_tx_cnt >= STGE_TX_HIWAT) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + /* + * Pack the data into the transmit ring. If we + * don't have room, set the OACTIVE flag and wait + * for the NIC to drain the ring. + */ + if (stge_encap(sc, &m_head)) { + if (m_head == NULL) + break; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + enq++; + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, m_head); + } + + if (enq > 0) { + /* Transmit */ + CSR_WRITE_4(sc, STGE_DMACtrl, DMAC_TxDMAPollNow); + + /* Set a timeout in case the chip goes out to lunch. */ + ifp->if_timer = 5; + } +} + +/* + * stge_watchdog: [ifnet interface function] + * + * Watchdog timer handler. + */ +static void +stge_watchdog(struct ifnet *ifp) +{ + struct stge_softc *sc; + + sc = ifp->if_softc; + + STGE_LOCK(sc); + if_printf(sc->sc_ifp, "device timeout\n"); + ifp->if_oerrors++; + stge_init_locked(sc); + STGE_UNLOCK(sc); +} + +/* + * stge_ioctl: [ifnet interface function] + * + * Handle control requests from the operator. + */ +static int +stge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct stge_softc *sc; + struct ifreq *ifr; + struct mii_data *mii; + int error, mask; + + sc = ifp->if_softc; + ifr = (struct ifreq *)data; + error = 0; + switch (cmd) { + case SIOCSIFMTU: + if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > STGE_JUMBO_MTU) + error = EINVAL; + else if (ifp->if_mtu != ifr->ifr_mtu) { + ifp->if_mtu = ifr->ifr_mtu; + STGE_LOCK(sc); + stge_init_locked(sc); + STGE_UNLOCK(sc); + } + break; + case SIOCSIFFLAGS: + STGE_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if (((ifp->if_flags ^ sc->sc_if_flags) + & IFF_PROMISC) != 0) + stge_set_filter(sc); + } else { + if (sc->sc_detach == 0) + stge_init_locked(sc); + } + } else { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + stge_stop(sc); + } + sc->sc_if_flags = ifp->if_flags; + STGE_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + STGE_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + stge_set_multi(sc); + STGE_UNLOCK(sc); + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + mii = device_get_softc(sc->sc_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + case SIOCSIFCAP: + mask = ifr->ifr_reqcap ^ ifp->if_capenable; +#ifdef DEVICE_POLLING + if ((mask & IFCAP_POLLING) != 0) { + if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) { + error = ether_poll_register(stge_poll, ifp); + if (error != 0) + break; + STGE_LOCK(sc); + CSR_WRITE_2(sc, STGE_IntEnable, 0); + ifp->if_capenable |= IFCAP_POLLING; + STGE_UNLOCK(sc); + } else { + error = ether_poll_deregister(ifp); + if (error != 0) + break; + STGE_LOCK(sc); + CSR_WRITE_2(sc, STGE_IntEnable, + sc->sc_IntEnable); + ifp->if_capenable &= ~IFCAP_POLLING; + STGE_UNLOCK(sc); + } + } +#endif + if ((mask & IFCAP_HWCSUM) != 0) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if ((IFCAP_HWCSUM & ifp->if_capenable) != 0 && + (IFCAP_HWCSUM & ifp->if_capabilities) != 0) + ifp->if_hwassist = STGE_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + } + if ((mask & IFCAP_VLAN_HWTAGGING) != 0) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + STGE_LOCK(sc); + stge_vlan_setup(sc); + STGE_UNLOCK(sc); + } + } + VLAN_CAPABILITIES(ifp); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +static void +stge_link_task(void *arg, int pending) +{ + struct stge_softc *sc; + uint32_t v, ac; + int i; + + sc = (struct stge_softc *)arg; + STGE_LOCK(sc); + /* + * Update STGE_MACCtrl register depending on link status. + * (duplex, flow control etc) + */ + v = ac = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + v &= ~(MC_DuplexSelect|MC_RxFlowControlEnable|MC_TxFlowControlEnable); + v |= sc->sc_MACCtrl; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + if (((ac ^ sc->sc_MACCtrl) & MC_DuplexSelect) != 0) { + /* Duplex setting changed, reset Tx/Rx functions. */ + ac = CSR_READ_4(sc, STGE_AsicCtrl); + ac |= AC_TxReset | AC_RxReset; + CSR_WRITE_4(sc, STGE_AsicCtrl, ac); + for (i = 0; i < STGE_TIMEOUT; i++) { + DELAY(100); + if ((CSR_READ_4(sc, STGE_AsicCtrl) & AC_ResetBusy) == 0) + break; + } + if (i == STGE_TIMEOUT) + device_printf(sc->sc_dev, "reset failed to complete\n"); + } + STGE_UNLOCK(sc); +} + +static __inline int +stge_tx_error(struct stge_softc *sc) +{ + uint32_t txstat; + int error; + + for (error = 0;;) { + txstat = CSR_READ_4(sc, STGE_TxStatus); + if ((txstat & TS_TxComplete) == 0) + break; + /* Tx underrun */ + if ((txstat & TS_TxUnderrun) != 0) { + /* + * XXX + * There should be a more better way to recover + * from Tx underrun instead of a full reset. + */ + if (sc->sc_nerr++ < STGE_MAXERR) + device_printf(sc->sc_dev, "Tx underrun, " + "resetting...\n"); + if (sc->sc_nerr == STGE_MAXERR) + device_printf(sc->sc_dev, "too many errors; " + "not reporting any more\n"); + error = -1; + break; + } + /* Maximum/Late collisions, Re-enable Tx MAC. */ + if ((txstat & (TS_MaxCollisions|TS_LateCollision)) != 0) + CSR_WRITE_4(sc, STGE_MACCtrl, + (CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK) | + MC_TxEnable); + } + + return (error); +} + +/* + * stge_intr: + * + * Interrupt service routine. + */ +static void +stge_intr(void *arg) +{ + struct stge_softc *sc; + struct ifnet *ifp; + int reinit; + uint16_t status; + + sc = (struct stge_softc *)arg; + ifp = sc->sc_ifp; + + STGE_LOCK(sc); + +#ifdef DEVICE_POLLING + if ((ifp->if_capenable & IFCAP_POLLING) != 0) + goto done_locked; +#endif + status = CSR_READ_2(sc, STGE_IntStatus); + if (sc->sc_suspended || (status & IS_InterruptStatus) == 0) + goto done_locked; + + /* Disable interrupts. */ + for (reinit = 0;;) { + status = CSR_READ_2(sc, STGE_IntStatusAck); + status &= sc->sc_IntEnable; + if (status == 0) + break; + /* Host interface errors. */ + if ((status & IS_HostError) != 0) { + device_printf(sc->sc_dev, + "Host interface error, resetting...\n"); + reinit = 1; + goto force_init; + } + + /* Receive interrupts. */ + if ((status & IS_RxDMAComplete) != 0) { + stge_rxeof(sc); + if ((status & IS_RFDListEnd) != 0) + CSR_WRITE_4(sc, STGE_DMACtrl, + DMAC_RxDMAPollNow); + } + + /* Transmit interrupts. */ + if ((status & (IS_TxDMAComplete | IS_TxComplete)) != 0) + stge_txeof(sc); + + /* Transmission errors.*/ + if ((status & IS_TxComplete) != 0) { + if ((reinit = stge_tx_error(sc)) != 0) + break; + } + } + +force_init: + if (reinit != 0) + stge_init_locked(sc); + + /* Re-enable interrupts. */ + CSR_WRITE_2(sc, STGE_IntEnable, sc->sc_IntEnable); + + /* Try to get more packets going. */ + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + stge_start_locked(ifp); + +done_locked: + STGE_UNLOCK(sc); +} + +/* + * stge_txeof: + * + * Helper; handle transmit interrupts. + */ +static void +stge_txeof(struct stge_softc *sc) +{ + struct ifnet *ifp; + struct stge_txdesc *txd; + uint64_t control; + int cons; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + + txd = STAILQ_FIRST(&sc->sc_cdata.stge_txbusyq); + if (txd == NULL) + return; + bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map, BUS_DMASYNC_POSTREAD); + + /* + * Go through our Tx list and free mbufs for those + * frames which have been transmitted. + */ + for (cons = sc->sc_cdata.stge_tx_cons;; + cons = (cons + 1) % STGE_TX_RING_CNT) { + if (sc->sc_cdata.stge_tx_cnt <= 0) + break; + control = le64toh(sc->sc_rdata.stge_tx_ring[cons].tfd_control); + if ((control & TFD_TFDDone) == 0) + break; + sc->sc_cdata.stge_tx_cnt--; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + bus_dmamap_sync(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap); + + /* Output counter is updated with statistics register */ + m_freem(txd->tx_m); + txd->tx_m = NULL; + STAILQ_REMOVE_HEAD(&sc->sc_cdata.stge_txbusyq, tx_q); + STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txfreeq, txd, tx_q); + txd = STAILQ_FIRST(&sc->sc_cdata.stge_txbusyq); + } + sc->sc_cdata.stge_tx_cons = cons; + if (sc->sc_cdata.stge_tx_cnt == 0) + ifp->if_timer = 0; + + bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +} + +static __inline void +stge_discard_rxbuf(struct stge_softc *sc, int idx) +{ + struct stge_rfd *rfd; + + rfd = &sc->sc_rdata.stge_rx_ring[idx]; + rfd->rfd_status = 0; +} + +#ifndef __NO_STRICT_ALIGNMENT +/* + * It seems that TC9021's DMA engine has alignment restrictions in + * DMA scatter operations. The first DMA segment has no address + * alignment restrictins but the rest should be aligned on 4(?) bytes + * boundary. Otherwise it would corrupt random memory. Since we don't + * know which one is used for the first segment in advance we simply + * don't align at all. + * To avoid copying over an entire frame to align, we allocate a new + * mbuf and copy ethernet header to the new mbuf. The new mbuf is + * prepended into the existing mbuf chain. + */ +static __inline struct mbuf * +stge_fixup_rx(struct stge_softc *sc, struct mbuf *m) +{ + struct mbuf *n; + + n = NULL; + if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) { + bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); + m->m_data += ETHER_HDR_LEN; + n = m; + } else { + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n != NULL) { + bcopy(m->m_data, n->m_data, ETHER_HDR_LEN); + m->m_data += ETHER_HDR_LEN; + m->m_len -= ETHER_HDR_LEN; + n->m_len = ETHER_HDR_LEN; + M_MOVE_PKTHDR(n, m); + n->m_next = m; + } else + m_freem(m); + } + + return (n); +} +#endif + +/* + * stge_rxeof: + * + * Helper; handle receive interrupts. + */ +static void +stge_rxeof(struct stge_softc *sc) +{ + struct ifnet *ifp; + struct stge_rxdesc *rxd; + struct mbuf *mp, *m; + uint64_t status64; + uint32_t status; + int cons, prog; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + + bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_cdata.stge_rx_ring_map, BUS_DMASYNC_POSTREAD); + + prog = 0; + for (cons = sc->sc_cdata.stge_rx_cons; prog < STGE_RX_RING_CNT; + prog++, cons = (cons + 1) % STGE_RX_RING_CNT) { + status64 = le64toh(sc->sc_rdata.stge_rx_ring[cons].rfd_status); + status = RFD_RxStatus(status64); + if ((status & RFD_RFDDone) == 0) + break; +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (sc->sc_cdata.stge_rxcycles <= 0) + break; + sc->sc_cdata.stge_rxcycles--; + } +#endif + prog++; + rxd = &sc->sc_cdata.stge_rxdesc[cons]; + mp = rxd->rx_m; + + /* + * If the packet had an error, drop it. Note we count + * the error later in the periodic stats update. + */ + if ((status & RFD_FrameEnd) != 0 && (status & + (RFD_RxFIFOOverrun | RFD_RxRuntFrame | + RFD_RxAlignmentError | RFD_RxFCSError | + RFD_RxLengthError)) != 0) { + stge_discard_rxbuf(sc, cons); + if (sc->sc_cdata.stge_rxhead != NULL) { + m_freem(sc->sc_cdata.stge_rxhead); + STGE_RXCHAIN_RESET(sc); + } + continue; + } + /* + * Add a new receive buffer to the ring. + */ + if (stge_newbuf(sc, cons) != 0) { + ifp->if_iqdrops++; + stge_discard_rxbuf(sc, cons); + if (sc->sc_cdata.stge_rxhead != NULL) { + m_freem(sc->sc_cdata.stge_rxhead); + STGE_RXCHAIN_RESET(sc); + } + continue; + } + + if ((status & RFD_FrameEnd) != 0) + mp->m_len = RFD_RxDMAFrameLen(status) - + sc->sc_cdata.stge_rxlen; + sc->sc_cdata.stge_rxlen += mp->m_len; + + /* Chain mbufs. */ + if (sc->sc_cdata.stge_rxhead == NULL) { + sc->sc_cdata.stge_rxhead = mp; + sc->sc_cdata.stge_rxtail = mp; + } else { + mp->m_flags &= ~M_PKTHDR; + sc->sc_cdata.stge_rxtail->m_next = mp; + sc->sc_cdata.stge_rxtail = mp; + } + + if ((status & RFD_FrameEnd) != 0) { + m = sc->sc_cdata.stge_rxhead; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = sc->sc_cdata.stge_rxlen; + + if (m->m_pkthdr.len > sc->sc_if_framesize) { + m_freem(m); + STGE_RXCHAIN_RESET(sc); + continue; + } + /* + * Set the incoming checksum information for + * the packet. + */ + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + if ((status & RFD_IPDetected) != 0) { + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED; + if ((status & RFD_IPError) == 0) + m->m_pkthdr.csum_flags |= + CSUM_IP_VALID; + } + if (((status & RFD_TCPDetected) != 0 && + (status & RFD_TCPError) == 0) || + ((status & RFD_UDPDetected) != 0 && + (status & RFD_UDPError) == 0)) { + m->m_pkthdr.csum_flags |= + (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + m->m_pkthdr.csum_data = 0xffff; + } + } + +#ifndef __NO_STRICT_ALIGNMENT + if (sc->sc_if_framesize > (MCLBYTES - ETHER_ALIGN)) { + if ((m = stge_fixup_rx(sc, m)) == NULL) { + STGE_RXCHAIN_RESET(sc); + continue; + } + } +#endif + /* Check for VLAN tagged packets. */ + if ((status & RFD_VLANDetected) != 0 && + (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) + VLAN_INPUT_TAG(ifp, m, RFD_TCI(status64)); + + STGE_UNLOCK(sc); + /* Pass it on. */ + (*ifp->if_input)(ifp, m); + STGE_LOCK(sc); + + STGE_RXCHAIN_RESET(sc); + } + } + + if (prog > 0) { + /* Update the consumer index. */ + sc->sc_cdata.stge_rx_cons = cons; + bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_cdata.stge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +} + +#ifdef DEVICE_POLLING +static void +stge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct stge_softc *sc; + uint16_t status; + + sc = ifp->if_softc; + STGE_LOCK(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + STGE_UNLOCK(sc); + return; + } + + sc->sc_cdata.stge_rxcycles = count; + stge_rxeof(sc); + stge_txeof(sc); + + if (cmd == POLL_AND_CHECK_STATUS) { + status = CSR_READ_2(sc, STGE_IntStatus); + status &= sc->sc_IntEnable; + if (status != 0) { + if ((status & IS_HostError) != 0) { + device_printf(sc->sc_dev, + "Host interface error, resetting...\n"); + stge_init_locked(sc); + } + if ((status & IS_TxComplete) != 0) { + if (stge_tx_error(sc) != 0) + stge_init_locked(sc); + } + } + + } + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + stge_start_locked(ifp); + + STGE_UNLOCK(sc); +} +#endif /* DEVICE_POLLING */ + +/* + * stge_tick: + * + * One second timer, used to tick the MII. + */ +static void +stge_tick(void *arg) +{ + struct stge_softc *sc; + struct mii_data *mii; + + sc = (struct stge_softc *)arg; + + STGE_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->sc_miibus); + mii_tick(mii); + + /* Update statistics counters. */ + stge_stats_update(sc); + + /* + * Relcaim any pending Tx descriptors to release mbufs in a + * timely manner as we don't generate Tx completion interrupts + * for every frame. This limits the delay to a maximum of one + * second. + */ + if (sc->sc_cdata.stge_tx_cnt != 0) + stge_txeof(sc); + + callout_reset(&sc->sc_tick_ch, hz, stge_tick, sc); +} + +/* + * stge_stats_update: + * + * Read the TC9021 statistics counters. + */ +static void +stge_stats_update(struct stge_softc *sc) +{ + struct ifnet *ifp; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + + CSR_READ_4(sc,STGE_OctetRcvOk); + + ifp->if_ipackets += CSR_READ_4(sc, STGE_FramesRcvdOk); + + ifp->if_ierrors += CSR_READ_2(sc, STGE_FramesLostRxErrors); + + CSR_READ_4(sc, STGE_OctetXmtdOk); + + ifp->if_opackets += CSR_READ_4(sc, STGE_FramesXmtdOk); + + ifp->if_collisions += + CSR_READ_4(sc, STGE_LateCollisions) + + CSR_READ_4(sc, STGE_MultiColFrames) + + CSR_READ_4(sc, STGE_SingleColFrames); + + ifp->if_oerrors += + CSR_READ_2(sc, STGE_FramesAbortXSColls) + + CSR_READ_2(sc, STGE_FramesWEXDeferal); +} + +/* + * stge_reset: + * + * Perform a soft reset on the TC9021. + */ +static void +stge_reset(struct stge_softc *sc, uint32_t how) +{ + uint32_t ac; + uint8_t v; + int i, dv; + + STGE_LOCK_ASSERT(sc); + + dv = 5000; + ac = CSR_READ_4(sc, STGE_AsicCtrl); + switch (how) { + case STGE_RESET_TX: + ac |= AC_TxReset | AC_FIFO; + dv = 100; + break; + case STGE_RESET_RX: + ac |= AC_RxReset | AC_FIFO; + dv = 100; + break; + case STGE_RESET_FULL: + default: + /* + * Only assert RstOut if we're fiber. We need GMII clocks + * to be present in order for the reset to complete on fiber + * cards. + */ + ac |= AC_GlobalReset | AC_RxReset | AC_TxReset | + AC_DMA | AC_FIFO | AC_Network | AC_Host | AC_AutoInit | + (sc->sc_usefiber ? AC_RstOut : 0); + break; + } + + CSR_WRITE_4(sc, STGE_AsicCtrl, ac); + + /* Account for reset problem at 10Mbps. */ + DELAY(dv); + + for (i = 0; i < STGE_TIMEOUT; i++) { + if ((CSR_READ_4(sc, STGE_AsicCtrl) & AC_ResetBusy) == 0) + break; + DELAY(dv); + } + + if (i == STGE_TIMEOUT) + device_printf(sc->sc_dev, "reset failed to complete\n"); + + /* Set LED, from Linux IPG driver. */ + ac = CSR_READ_4(sc, STGE_AsicCtrl); + ac &= ~(AC_LEDMode | AC_LEDSpeed | AC_LEDModeBit1); + if ((sc->sc_led & 0x01) != 0) + ac |= AC_LEDMode; + if ((sc->sc_led & 0x03) != 0) + ac |= AC_LEDModeBit1; + if ((sc->sc_led & 0x08) != 0) + ac |= AC_LEDSpeed; + CSR_WRITE_4(sc, STGE_AsicCtrl, ac); + + /* Set PHY, from Linux IPG driver */ + v = CSR_READ_1(sc, STGE_PhySet); + v &= ~(PS_MemLenb9b | PS_MemLen | PS_NonCompdet); + v |= ((sc->sc_led & 0x70) >> 4); + CSR_WRITE_1(sc, STGE_PhySet, v); +} + +/* + * stge_init: [ ifnet interface function ] + * + * Initialize the interface. + */ +static void +stge_init(void *xsc) +{ + struct stge_softc *sc; + + sc = (struct stge_softc *)xsc; + STGE_LOCK(sc); + stge_init_locked(sc); + STGE_UNLOCK(sc); +} + +static void +stge_init_locked(struct stge_softc *sc) +{ + struct ifnet *ifp; + struct mii_data *mii; + uint16_t eaddr[3]; + uint32_t v; + int error; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + mii = device_get_softc(sc->sc_miibus); + + /* + * Cancel any pending I/O. + */ + stge_stop(sc); + + /* Init descriptors. */ + error = stge_init_rx_ring(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "initialization failed: no memory for rx buffers\n"); + stge_stop(sc); + goto out; + } + stge_init_tx_ring(sc); + + /* Set the station address. */ + bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); + CSR_WRITE_2(sc, STGE_StationAddress0, htole16(eaddr[0])); + CSR_WRITE_2(sc, STGE_StationAddress1, htole16(eaddr[1])); + CSR_WRITE_2(sc, STGE_StationAddress2, htole16(eaddr[2])); + + /* + * Set the statistics masks. Disable all the RMON stats, + * and disable selected stats in the non-RMON stats registers. + */ + CSR_WRITE_4(sc, STGE_RMONStatisticsMask, 0xffffffff); + CSR_WRITE_4(sc, STGE_StatisticsMask, + (1U << 1) | (1U << 2) | (1U << 3) | (1U << 4) | (1U << 5) | + (1U << 6) | (1U << 7) | (1U << 8) | (1U << 9) | (1U << 10) | + (1U << 13) | (1U << 14) | (1U << 15) | (1U << 19) | (1U << 20) | + (1U << 21)); + + /* Set up the receive filter. */ + stge_set_filter(sc); + /* Program multicast filter. */ + stge_set_multi(sc); + + /* + * Give the transmit and receive ring to the chip. + */ + CSR_WRITE_4(sc, STGE_TFDListPtrHi, + STGE_ADDR_HI(STGE_TX_RING_ADDR(sc, 0))); + CSR_WRITE_4(sc, STGE_TFDListPtrLo, + STGE_ADDR_LO(STGE_TX_RING_ADDR(sc, 0))); + + CSR_WRITE_4(sc, STGE_RFDListPtrHi, + STGE_ADDR_HI(STGE_RX_RING_ADDR(sc, 0))); + CSR_WRITE_4(sc, STGE_RFDListPtrLo, + STGE_ADDR_LO(STGE_RX_RING_ADDR(sc, 0))); + + /* + * Initialize the Tx auto-poll period. It's OK to make this number + * large (255 is the max, but we use 127) -- we explicitly kick the + * transmit engine when there's actually a packet. + */ + CSR_WRITE_1(sc, STGE_TxDMAPollPeriod, 127); + + /* ..and the Rx auto-poll period. */ + CSR_WRITE_1(sc, STGE_RxDMAPollPeriod, 1); + + /* Initialize the Tx start threshold. */ + CSR_WRITE_2(sc, STGE_TxStartThresh, sc->sc_txthresh); + + /* Rx DMA thresholds, from Linux */ + CSR_WRITE_1(sc, STGE_RxDMABurstThresh, 0x30); + CSR_WRITE_1(sc, STGE_RxDMAUrgentThresh, 0x30); + + /* Rx early threhold, from Linux */ + CSR_WRITE_2(sc, STGE_RxEarlyThresh, 0x7ff); + + /* Tx DMA thresholds, from Linux */ + CSR_WRITE_1(sc, STGE_TxDMABurstThresh, 0x30); + CSR_WRITE_1(sc, STGE_TxDMAUrgentThresh, 0x04); + + /* + * Initialize the Rx DMA interrupt control register. We + * request an interrupt after every incoming packet, but + * defer it for sc_rxint_dmawait us. When the number of + * interrupts pending reaches STGE_RXINT_NFRAME, we stop + * deferring the interrupt, and signal it immediately. + */ + CSR_WRITE_4(sc, STGE_RxDMAIntCtrl, + RDIC_RxFrameCount(sc->sc_rxint_nframe) | + RDIC_RxDMAWaitTime(STGE_RXINT_USECS2TICK(sc->sc_rxint_dmawait))); + + /* + * Initialize the interrupt mask. + */ + sc->sc_IntEnable = IS_HostError | IS_TxComplete | + IS_TxDMAComplete | IS_RxDMAComplete | IS_RFDListEnd; +#ifdef DEVICE_POLLING + /* Disable interrupts if we are polling. */ + if ((ifp->if_capenable & IFCAP_POLLING) != 0) + CSR_WRITE_2(sc, STGE_IntEnable, 0); + else +#endif + CSR_WRITE_2(sc, STGE_IntEnable, sc->sc_IntEnable); + + /* + * Configure the DMA engine. + * XXX Should auto-tune TxBurstLimit. + */ + CSR_WRITE_4(sc, STGE_DMACtrl, sc->sc_DMACtrl | DMAC_TxBurstLimit(3)); + + /* + * Send a PAUSE frame when we reach 29,696 bytes in the Rx + * FIFO, and send an un-PAUSE frame when we reach 3056 bytes + * in the Rx FIFO. + */ + CSR_WRITE_2(sc, STGE_FlowOnTresh, 29696 / 16); + CSR_WRITE_2(sc, STGE_FlowOffThresh, 3056 / 16); + + /* + * Set the maximum frame size. + */ + sc->sc_if_framesize = ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; + CSR_WRITE_2(sc, STGE_MaxFrameSize, sc->sc_if_framesize); + + /* + * Initialize MacCtrl -- do it before setting the media, + * as setting the media will actually program the register. + * + * Note: We have to poke the IFS value before poking + * anything else. + */ + /* Tx/Rx MAC should be disabled before programming IFS.*/ + CSR_WRITE_4(sc, STGE_MACCtrl, MC_IFSSelect(MC_IFS96bit)); + + stge_vlan_setup(sc); + + if (sc->sc_rev >= 6) { /* >= B.2 */ + /* Multi-frag frame bug work-around. */ + CSR_WRITE_2(sc, STGE_DebugCtrl, + CSR_READ_2(sc, STGE_DebugCtrl) | 0x0200); + + /* Tx Poll Now bug work-around. */ + CSR_WRITE_2(sc, STGE_DebugCtrl, + CSR_READ_2(sc, STGE_DebugCtrl) | 0x0010); + /* Tx Poll Now bug work-around. */ + CSR_WRITE_2(sc, STGE_DebugCtrl, + CSR_READ_2(sc, STGE_DebugCtrl) | 0x0020); + } + + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + v |= MC_StatisticsEnable | MC_TxEnable | MC_RxEnable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + /* + * It seems that transmitting frames without checking the state of + * Rx/Tx MAC wedge the hardware. + */ + stge_start_tx(sc); + stge_start_rx(sc); + + /* + * Set the current media. + */ + mii_mediachg(mii); + + /* + * Start the one second MII clock. + */ + callout_reset(&sc->sc_tick_ch, hz, stge_tick, sc); + + /* + * ...all done! + */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + out: + if (error != 0) + device_printf(sc->sc_dev, "interface not running\n"); +} + +static void +stge_vlan_setup(struct stge_softc *sc) +{ + struct ifnet *ifp; + uint32_t v; + + ifp = sc->sc_ifp; + /* + * The NIC always copy a VLAN tag regardless of STGE_MACCtrl + * MC_AutoVLANuntagging bit. + * MC_AutoVLANtagging bit selects which VLAN source to use + * between STGE_VLANTag and TFC. However TFC TFD_VLANTagInsert + * bit has priority over MC_AutoVLANtagging bit. So we always + * use TFC instead of STGE_VLANTag register. + */ + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) + v |= MC_AutoVLANuntagging; + else + v &= ~MC_AutoVLANuntagging; + CSR_WRITE_4(sc, STGE_MACCtrl, v); +} + +/* + * Stop transmission on the interface. + */ +static void +stge_stop(struct stge_softc *sc) +{ + struct ifnet *ifp; + struct stge_txdesc *txd; + struct stge_rxdesc *rxd; + uint32_t v; + int i; + + STGE_LOCK_ASSERT(sc); + /* + * Stop the one second clock. + */ + callout_stop(&sc->sc_tick_ch); + + /* + * Reset the chip to a known state. + */ + stge_reset(sc, STGE_RESET_FULL); + + /* + * Disable interrupts. + */ + CSR_WRITE_2(sc, STGE_IntEnable, 0); + + /* + * Stop receiver, transmitter, and stats update. + */ + stge_stop_rx(sc); + stge_stop_tx(sc); + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + v |= MC_StatisticsDisable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + + /* + * Stop the transmit and receive DMA. + */ + stge_dma_wait(sc); + CSR_WRITE_4(sc, STGE_TFDListPtrHi, 0); + CSR_WRITE_4(sc, STGE_TFDListPtrLo, 0); + CSR_WRITE_4(sc, STGE_RFDListPtrHi, 0); + CSR_WRITE_4(sc, STGE_RFDListPtrLo, 0); + + /* + * Free RX and TX mbufs still in the queues. + */ + for (i = 0; i < STGE_RX_RING_CNT; i++) { + rxd = &sc->sc_cdata.stge_rxdesc[i]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->sc_cdata.stge_rx_tag, + rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_cdata.stge_rx_tag, + rxd->rx_dmamap); + m_freem(rxd->rx_m); + rxd->rx_m = NULL; + } + } + for (i = 0; i < STGE_TX_RING_CNT; i++) { + txd = &sc->sc_cdata.stge_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_sync(sc->sc_cdata.stge_tx_tag, + txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_cdata.stge_tx_tag, + txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + } + } + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp = sc->sc_ifp; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + ifp->if_timer = 0; +} + +static void +stge_start_tx(struct stge_softc *sc) +{ + uint32_t v; + int i; + + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_TxEnabled) != 0) + return; + v |= MC_TxEnable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + CSR_WRITE_1(sc, STGE_TxDMAPollPeriod, 127); + for (i = STGE_TIMEOUT; i > 0; i--) { + DELAY(10); + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_TxEnabled) != 0) + break; + } + if (i == 0) + device_printf(sc->sc_dev, "Starting Tx MAC timed out\n"); +} + +static void +stge_start_rx(struct stge_softc *sc) +{ + uint32_t v; + int i; + + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_RxEnabled) != 0) + return; + v |= MC_RxEnable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + CSR_WRITE_1(sc, STGE_RxDMAPollPeriod, 1); + for (i = STGE_TIMEOUT; i > 0; i--) { + DELAY(10); + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_RxEnabled) != 0) + break; + } + if (i == 0) + device_printf(sc->sc_dev, "Starting Rx MAC timed out\n"); +} + +static void +stge_stop_tx(struct stge_softc *sc) +{ + uint32_t v; + int i; + + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_TxEnabled) == 0) + return; + v |= MC_TxDisable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + for (i = STGE_TIMEOUT; i > 0; i--) { + DELAY(10); + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_TxEnabled) == 0) + break; + } + if (i == 0) + device_printf(sc->sc_dev, "Stopping Tx MAC timed out\n"); +} + +static void +stge_stop_rx(struct stge_softc *sc) +{ + uint32_t v; + int i; + + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_RxEnabled) == 0) + return; + v |= MC_RxDisable; + CSR_WRITE_4(sc, STGE_MACCtrl, v); + for (i = STGE_TIMEOUT; i > 0; i--) { + DELAY(10); + v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK; + if ((v & MC_RxEnabled) == 0) + break; + } + if (i == 0) + device_printf(sc->sc_dev, "Stopping Rx MAC timed out\n"); +} + +static void +stge_init_tx_ring(struct stge_softc *sc) +{ + struct stge_ring_data *rd; + struct stge_txdesc *txd; + bus_addr_t addr; + int i; + + STAILQ_INIT(&sc->sc_cdata.stge_txfreeq); + STAILQ_INIT(&sc->sc_cdata.stge_txbusyq); + + sc->sc_cdata.stge_tx_prod = 0; + sc->sc_cdata.stge_tx_cons = 0; + sc->sc_cdata.stge_tx_cnt = 0; + + rd = &sc->sc_rdata; + bzero(rd->stge_tx_ring, STGE_TX_RING_SZ); + for (i = 0; i < STGE_TX_RING_CNT; i++) { + if (i == (STGE_TX_RING_CNT - 1)) + addr = STGE_TX_RING_ADDR(sc, 0); + else + addr = STGE_TX_RING_ADDR(sc, i + 1); + rd->stge_tx_ring[i].tfd_next = htole64(addr); + rd->stge_tx_ring[i].tfd_control = htole64(TFD_TFDDone); + txd = &sc->sc_cdata.stge_txdesc[i]; + STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txfreeq, txd, tx_q); + } + + bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag, + sc->sc_cdata.stge_tx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + +} + +static int +stge_init_rx_ring(struct stge_softc *sc) +{ + struct stge_ring_data *rd; + bus_addr_t addr; + int i; + + sc->sc_cdata.stge_rx_cons = 0; + STGE_RXCHAIN_RESET(sc); + + rd = &sc->sc_rdata; + bzero(rd->stge_rx_ring, STGE_RX_RING_SZ); + for (i = 0; i < STGE_RX_RING_CNT; i++) { + if (stge_newbuf(sc, i) != 0) + return (ENOBUFS); + if (i == (STGE_RX_RING_CNT - 1)) + addr = STGE_RX_RING_ADDR(sc, 0); + else + addr = STGE_RX_RING_ADDR(sc, i + 1); + rd->stge_rx_ring[i].rfd_next = htole64(addr); + rd->stge_rx_ring[i].rfd_status = 0; + } + + bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag, + sc->sc_cdata.stge_rx_ring_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * stge_newbuf: + * + * Add a receive buffer to the indicated descriptor. + */ +static int +stge_newbuf(struct stge_softc *sc, int idx) +{ + struct stge_rxdesc *rxd; + struct stge_rfd *rfd; + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + int nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + m->m_len = m->m_pkthdr.len = MCLBYTES; + /* + * The hardware requires 4bytes aligned DMA address when JUMBO + * frame is used. + */ + if (sc->sc_if_framesize <= (MCLBYTES - ETHER_ALIGN)) + m_adj(m, ETHER_ALIGN); + + if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_rx_tag, + sc->sc_cdata.stge_rx_sparemap, m, segs, &nsegs, 0) != 0) { + m_freem(m); + return (ENOBUFS); + } + KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); + + rxd = &sc->sc_cdata.stge_rxdesc[idx]; + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->sc_cdata.stge_rx_sparemap; + sc->sc_cdata.stge_rx_sparemap = map; + bus_dmamap_sync(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap, + BUS_DMASYNC_PREREAD); + rxd->rx_m = m; + + rfd = &sc->sc_rdata.stge_rx_ring[idx]; + rfd->rfd_frag.frag_word0 = + htole64(FRAG_ADDR(segs[0].ds_addr) | FRAG_LEN(segs[0].ds_len)); + rfd->rfd_status = 0; + + return (0); +} + +/* + * stge_set_filter: + * + * Set up the receive filter. + */ +static void +stge_set_filter(struct stge_softc *sc) +{ + struct ifnet *ifp; + uint16_t mode; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + + mode = CSR_READ_2(sc, STGE_ReceiveMode); + mode |= RM_ReceiveUnicast; + if ((ifp->if_flags & IFF_BROADCAST) != 0) + mode |= RM_ReceiveBroadcast; + else + mode &= ~RM_ReceiveBroadcast; + if ((ifp->if_flags & IFF_PROMISC) != 0) + mode |= RM_ReceiveAllFrames; + else + mode &= ~RM_ReceiveAllFrames; + + CSR_WRITE_2(sc, STGE_ReceiveMode, mode); +} + +static void +stge_set_multi(struct stge_softc *sc) +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint32_t crc; + uint32_t mchash[2]; + uint16_t mode; + int count; + + STGE_LOCK_ASSERT(sc); + + ifp = sc->sc_ifp; + + mode = CSR_READ_2(sc, STGE_ReceiveMode); + if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { + if ((ifp->if_flags & IFF_PROMISC) != 0) + mode |= RM_ReceiveAllFrames; + else if ((ifp->if_flags & IFF_ALLMULTI) != 0) + mode |= RM_ReceiveMulticast; + CSR_WRITE_2(sc, STGE_ReceiveMode, mode); + return; + } + + /* clear existing filters. */ + CSR_WRITE_4(sc, STGE_HashTable0, 0); + CSR_WRITE_4(sc, STGE_HashTable1, 0); + + /* + * Set up the multicast address filter by passing all multicast + * addresses through a CRC generator, and then using the low-order + * 6 bits as an index into the 64 bit multicast hash table. The + * high order bits select the register, while the rest of the bits + * select the bit within the register. + */ + + bzero(mchash, sizeof(mchash)); + + count = 0; + IF_ADDR_LOCK(sc->sc_ifp); + TAILQ_FOREACH(ifma, &sc->sc_ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN); + + /* Just want the 6 least significant bits. */ + crc &= 0x3f; + + /* Set the corresponding bit in the hash table. */ + mchash[crc >> 5] |= 1 << (crc & 0x1f); + count++; + } + IF_ADDR_UNLOCK(ifp); + + mode &= ~(RM_ReceiveMulticast | RM_ReceiveAllFrames); + if (count > 0) + mode |= RM_ReceiveMulticastHash; + else + mode &= ~RM_ReceiveMulticastHash; + + CSR_WRITE_4(sc, STGE_HashTable0, mchash[0]); + CSR_WRITE_4(sc, STGE_HashTable1, mchash[1]); + CSR_WRITE_2(sc, STGE_ReceiveMode, mode); +} + +static int +sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) +{ + int error, value; + + if (!arg1) + return (EINVAL); + value = *(int *)arg1; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error || !req->newptr) + return (error); + if (value < low || value > high) + return (EINVAL); + *(int *)arg1 = value; + + return (0); +} + +static int +sysctl_hw_stge_rxint_nframe(SYSCTL_HANDLER_ARGS) +{ + return (sysctl_int_range(oidp, arg1, arg2, req, + STGE_RXINT_NFRAME_MIN, STGE_RXINT_NFRAME_MAX)); +} + +static int +sysctl_hw_stge_rxint_dmawait(SYSCTL_HANDLER_ARGS) +{ + return (sysctl_int_range(oidp, arg1, arg2, req, + STGE_RXINT_DMAWAIT_MIN, STGE_RXINT_DMAWAIT_MAX)); +} diff --git a/sys/dev/stge/if_stgereg.h b/sys/dev/stge/if_stgereg.h new file mode 100644 index 000000000000..135443955048 --- /dev/null +++ b/sys/dev/stge/if_stgereg.h @@ -0,0 +1,697 @@ +/* $NetBSD: if_stgereg.h,v 1.3 2003/02/10 21:10:07 christos Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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. + */ + +/* $FreeBSD$ */ + +/* + * Sundance Technology PCI vendor ID + */ +#define VENDOR_SUNDANCETI 0x13f0 + +/* + * Tamarack Microelectronics PCI vendor ID + */ +#define VENDOR_TAMARACK 0x143d + +/* + * D-Link Systems PCI vendor ID + */ +#define VENDOR_DLINK 0x1186 + +/* + * Antares Microsystems PCI vendor ID + */ +#define VENDOR_ANTARES 0x1754 + +/* + * Sundance Technology device ID + */ +#define DEVICEID_SUNDANCETI_ST1023 0x1023 +#define DEVICEID_SUNDANCETI_ST2021 0x2021 +#define DEVICEID_TAMARACK_TC9021 0x1021 +#define DEVICEID_TAMARACK_TC9021_ALT 0x9021 + +/* + * D-Link Systems device ID + */ +#define DEVICEID_DLINK_DL4000 0x4000 + +/* + * Antares Microsystems device ID + */ +#define DEVICEID_ANTARES_TC9021 0x1021 + +/* + * Register description for the Sundance Tech. TC9021 10/100/1000 + * Ethernet controller. + * + * Note that while DMA addresses are all in 64-bit fields, only + * the lower 40 bits of a DMA address are valid. + */ +#if (BUS_SPACE_MAXADDR < 0xFFFFFFFFFF) +#define STGE_DMA_MAXADDR BUS_SPACE_MAXADDR +#else +#define STGE_DMA_MAXADDR 0xFFFFFFFFFF +#endif + +/* + * Register access macros + */ +#define CSR_WRITE_4(_sc, reg, val) \ + bus_write_4((_sc)->sc_res[0], (reg), (val)) +#define CSR_WRITE_2(_sc, reg, val) \ + bus_write_2((_sc)->sc_res[0], (reg), (val)) +#define CSR_WRITE_1(_sc, reg, val) \ + bus_write_1((_sc)->sc_res[0], (reg), (val)) + +#define CSR_READ_4(_sc, reg) \ + bus_read_4((_sc)->sc_res[0], (reg)) +#define CSR_READ_2(_sc, reg) \ + bus_read_2((_sc)->sc_res[0], (reg)) +#define CSR_READ_1(_sc, reg) \ + bus_read_1((_sc)->sc_res[0], (reg)) + +/* + * TC9021 buffer fragment descriptor. + */ +struct stge_frag { + uint64_t frag_word0; /* address, length */ +}; + +#define FRAG_ADDR(x) (((uint64_t)(x)) << 0) +#define FRAG_ADDR_MASK FRAG_ADDR(0xfffffffffULL) +#define FRAG_LEN(x) (((uint64_t)(x)) << 48) +#define FRAG_LEN_MASK FRAG_LEN(0xffffULL) + +/* + * TC9021 Transmit Frame Descriptor. Note the number of fragments + * here is arbitrary, but we can't have any more than 15. + */ +#define STGE_NTXFRAGS 15 +struct stge_tfd { + uint64_t tfd_next; /* next TFD in list */ + uint64_t tfd_control; /* control bits */ + /* the buffer fragments */ + struct stge_frag tfd_frags[STGE_NTXFRAGS]; +}; + +#define TFD_FrameId(x) ((x) << 0) +#define TFD_FrameId_MAX 0xffff +#define TFD_WordAlign(x) ((x) << 16) +#define TFD_WordAlign_dword 0 /* align to dword in TxFIFO */ +#define TFD_WordAlign_word 2 /* align to word in TxFIFO */ +#define TFD_WordAlign_disable 1 /* disable alignment */ +#define TFD_TCPChecksumEnable (1ULL << 18) +#define TFD_UDPChecksumEnable (1ULL << 19) +#define TFD_IPChecksumEnable (1ULL << 20) +#define TFD_FcsAppendDisable (1ULL << 21) +#define TFD_TxIndicate (1ULL << 22) +#define TFD_TxDMAIndicate (1ULL << 23) +#define TFD_FragCount(x) ((x) << 24) +#define TFD_VLANTagInsert (1ULL << 28) +#define TFD_TFDDone (1ULL << 31) +#define TFD_VID(x) (((uint64_t)(x)) << 32) +#define TFD_CFI (1ULL << 44) +#define TFD_UserPriority(x) (((uint64_t)(x)) << 45) + +/* + * TC9021 Receive Frame Descriptor. Each RFD has a single fragment + * in it, and the chip tells us the beginning and end of the frame. + */ +struct stge_rfd { + uint64_t rfd_next; /* next RFD in list */ + uint64_t rfd_status; /* status bits */ + struct stge_frag rfd_frag; /* the buffer */ +}; + +/* Low word of rfd_status */ +#define RFD_RxStatus(x) ((x) & 0xffffffff) +#define RFD_RxDMAFrameLen(x) ((x) & 0xffff) +#define RFD_RxFIFOOverrun 0x00010000 +#define RFD_RxRuntFrame 0x00020000 +#define RFD_RxAlignmentError 0x00040000 +#define RFD_RxFCSError 0x00080000 +#define RFD_RxOversizedFrame 0x00100000 +#define RFD_RxLengthError 0x00200000 +#define RFD_VLANDetected 0x00400000 +#define RFD_TCPDetected 0x00800000 +#define RFD_TCPError 0x01000000 +#define RFD_UDPDetected 0x02000000 +#define RFD_UDPError 0x04000000 +#define RFD_IPDetected 0x08000000 +#define RFD_IPError 0x10000000 +#define RFD_FrameStart 0x20000000 +#define RFD_FrameEnd 0x40000000 +#define RFD_RFDDone 0x80000000 +/* High word of rfd_status */ +#define RFD_TCI(x) ((((uint64_t)(x)) >> 32) & 0xffff) + +/* + * EEPROM offsets. + */ +#define STGE_EEPROM_ConfigParam 0x00 +#define STGE_EEPROM_AsicCtrl 0x01 +#define STGE_EEPROM_SubSystemVendorId 0x02 +#define STGE_EEPROM_SubSystemId 0x03 +#define STGE_EEPROM_LEDMode 0x06 +#define STGE_EEPROM_StationAddress0 0x10 +#define STGE_EEPROM_StationAddress1 0x11 +#define STGE_EEPROM_StationAddress2 0x12 + +/* + * The TC9021 register space. + */ + +#define STGE_DMACtrl 0x00 +#define DMAC_RxDMAComplete (1U << 3) +#define DMAC_RxDMAPollNow (1U << 4) +#define DMAC_TxDMAComplete (1U << 11) +#define DMAC_TxDMAPollNow (1U << 12) +#define DMAC_TxDMAInProg (1U << 15) +#define DMAC_RxEarlyDisable (1U << 16) +#define DMAC_MWIDisable (1U << 18) +#define DMAC_TxWriteBackDisable (1U << 19) +#define DMAC_TxBurstLimit(x) ((x) << 20) +#define DMAC_TargetAbort (1U << 30) +#define DMAC_MasterAbort (1U << 31) + +#define STGE_RxDMAStatus 0x08 + +#define STGE_TFDListPtrLo 0x10 + +#define STGE_TFDListPtrHi 0x14 + +#define STGE_TxDMABurstThresh 0x18 /* 8-bit */ + +#define STGE_TxDMAUrgentThresh 0x19 /* 8-bit */ + +#define STGE_TxDMAPollPeriod 0x1a /* 8-bit, 320ns increments */ + +#define STGE_RFDListPtrLo 0x1c + +#define STGE_RFDListPtrHi 0x20 + +#define STGE_RxDMABurstThresh 0x24 /* 8-bit */ + +#define STGE_RxDMAUrgentThresh 0x25 /* 8-bit */ + +#define STGE_RxDMAPollPeriod 0x26 /* 8-bit, 320ns increments */ + +#define STGE_RxDMAIntCtrl 0x28 +#define RDIC_RxFrameCount(x) ((x) & 0xff) +#define RDIC_PriorityThresh(x) ((x) << 10) +#define RDIC_RxDMAWaitTime(x) ((x) << 16) +/* + * Number of receive frames transferred via DMA before a Rx interrupt is issued. + */ +#define STGE_RXINT_NFRAME_DEFAULT 8 +#define STGE_RXINT_NFRAME_MIN 1 +#define STGE_RXINT_NFRAME_MAX 255 +/* + * Maximum amount of time (in 64ns increments) to wait before issuing a Rx + * interrupt if number of frames recevied is less than STGE_RXINT_NFRAME + * (STGE_RXINT_NFRAME_MIN <= STGE_RXINT_NFRAME <= STGE_RXINT_NFRAME_MAX) + */ +#define STGE_RXINT_DMAWAIT_DEFAULT 30 /* 30us */ +#define STGE_RXINT_DMAWAIT_MIN 0 +#define STGE_RXINT_DMAWAIT_MAX 4194 +#define STGE_RXINT_USECS2TICK(x) (((x) * 1000)/64) + +#define STGE_DebugCtrl 0x2c /* 16-bit */ +#define DC_GPIO0Ctrl (1U << 0) +#define DC_GPIO1Ctrl (1U << 1) +#define DC_GPIO0 (1U << 2) +#define DC_GPIO1 (1U << 3) + +#define STGE_AsicCtrl 0x30 +#define AC_ExpRomDisable (1U << 0) +#define AC_ExpRomSize (1U << 1) +#define AC_PhySpeed10 (1U << 4) +#define AC_PhySpeed100 (1U << 5) +#define AC_PhySpeed1000 (1U << 6) +#define AC_PhyMedia (1U << 7) +#define AC_ForcedConfig(x) ((x) << 8) +#define AC_ForcedConfig_MASK AC_ForcedConfig(7) +#define AC_D3ResetDisable (1U << 11) +#define AC_SpeedupMode (1U << 13) +#define AC_LEDMode (1U << 14) +#define AC_RstOutPolarity (1U << 15) +#define AC_GlobalReset (1U << 16) +#define AC_RxReset (1U << 17) +#define AC_TxReset (1U << 18) +#define AC_DMA (1U << 19) +#define AC_FIFO (1U << 20) +#define AC_Network (1U << 21) +#define AC_Host (1U << 22) +#define AC_AutoInit (1U << 23) +#define AC_RstOut (1U << 24) +#define AC_InterruptRequest (1U << 25) +#define AC_ResetBusy (1U << 26) +#define AC_LEDSpeed (1U << 27) +#define AC_LEDModeBit1 (1U << 29) + +#define STGE_FIFOCtrl 0x38 /* 16-bit */ +#define FC_RAMTestMode (1U << 0) +#define FC_Transmitting (1U << 14) +#define FC_Receiving (1U << 15) + +#define STGE_RxEarlyThresh 0x3a /* 16-bit */ + +#define STGE_FlowOffThresh 0x3c /* 16-bit */ + +#define STGE_FlowOnTresh 0x3e /* 16-bit */ + +#define STGE_TxStartThresh 0x44 /* 16-bit */ + +#define STGE_EepromData 0x48 /* 16-bit */ + +#define STGE_EepromCtrl 0x4a /* 16-bit */ +#define EC_EepromAddress(x) ((x) & 0xff) +#define EC_EepromOpcode(x) ((x) << 8) +#define EC_OP_WE 0 +#define EC_OP_WR 1 +#define EC_OP_RR 2 +#define EC_OP_ER 3 +#define EC_EepromBusy (1U << 15) + +#define STGE_ExpRomAddr 0x4c + +#define STGE_ExpRomData 0x50 /* 8-bit */ + +#define STGE_WakeEvent 0x51 /* 8-bit */ + +#define STGE_Countdown 0x54 +#define CD_Count(x) ((x) & 0xffff) +#define CD_CountdownSpeed (1U << 24) +#define CD_CountdownMode (1U << 25) +#define CD_CountdownIntEnabled (1U << 26) + +#define STGE_IntStatusAck 0x5a /* 16-bit */ + +#define STGE_IntEnable 0x5c /* 16-bit */ + +#define STGE_IntStatus 0x5e /* 16-bit */ + +#define IS_InterruptStatus (1U << 0) +#define IS_HostError (1U << 1) +#define IS_TxComplete (1U << 2) +#define IS_MACControlFrame (1U << 3) +#define IS_RxComplete (1U << 4) +#define IS_RxEarly (1U << 5) +#define IS_InRequested (1U << 6) +#define IS_UpdateStats (1U << 7) +#define IS_LinkEvent (1U << 8) +#define IS_TxDMAComplete (1U << 9) +#define IS_RxDMAComplete (1U << 10) +#define IS_RFDListEnd (1U << 11) +#define IS_RxDMAPriority (1U << 12) + +#define STGE_TxStatus 0x60 +#define TS_TxError (1U << 0) +#define TS_LateCollision (1U << 2) +#define TS_MaxCollisions (1U << 3) +#define TS_TxUnderrun (1U << 4) +#define TS_TxIndicateReqd (1U << 6) +#define TS_TxComplete (1U << 7) +#define TS_TxFrameId_get(x) ((x) >> 16) + +#define STGE_MACCtrl 0x6c +#define MC_IFSSelect(x) ((x) & 3) +#define MC_IFS96bit 0 +#define MC_IFS1024bit 1 +#define MC_IFS1792bit 2 +#define MC_IFS4352bit 3 + +#define MC_DuplexSelect (1U << 5) +#define MC_RcvLargeFrames (1U << 6) +#define MC_TxFlowControlEnable (1U << 7) +#define MC_RxFlowControlEnable (1U << 8) +#define MC_RcvFCS (1U << 9) +#define MC_FIFOLoopback (1U << 10) +#define MC_MACLoopback (1U << 11) +#define MC_AutoVLANtagging (1U << 12) +#define MC_AutoVLANuntagging (1U << 13) +#define MC_CollisionDetect (1U << 16) +#define MC_CarrierSense (1U << 17) +#define MC_StatisticsEnable (1U << 21) +#define MC_StatisticsDisable (1U << 22) +#define MC_StatisticsEnabled (1U << 23) +#define MC_TxEnable (1U << 24) +#define MC_TxDisable (1U << 25) +#define MC_TxEnabled (1U << 26) +#define MC_RxEnable (1U << 27) +#define MC_RxDisable (1U << 28) +#define MC_RxEnabled (1U << 29) +#define MC_Paused (1U << 30) +#define MC_MASK 0x7fe33fa3 + +#define STGE_VLANTag 0x70 + +#define STGE_PhySet 0x75 /* 8-bit */ +#define PS_MemLenb9b (1U << 0) +#define PS_MemLen (1U << 1) +#define PS_NonCompdet (1U << 2) + +#define STGE_PhyCtrl 0x76 /* 8-bit */ +#define PC_MgmtClk (1U << 0) +#define PC_MgmtData (1U << 1) +#define PC_MgmtDir (1U << 2) /* MAC->PHY */ +#define PC_PhyDuplexPolarity (1U << 3) +#define PC_PhyDuplexStatus (1U << 4) +#define PC_PhyLnkPolarity (1U << 5) +#define PC_LinkSpeed(x) (((x) >> 6) & 3) +#define PC_LinkSpeed_Down 0 +#define PC_LinkSpeed_10 1 +#define PC_LinkSpeed_100 2 +#define PC_LinkSpeed_1000 3 + +#define STGE_StationAddress0 0x78 /* 16-bit */ + +#define STGE_StationAddress1 0x7a /* 16-bit */ + +#define STGE_StationAddress2 0x7c /* 16-bit */ + +#define STGE_VLANHashTable 0x7e /* 16-bit */ + +#define STGE_VLANId 0x80 + +#define STGE_MaxFrameSize 0x86 + +#define STGE_ReceiveMode 0x88 /* 16-bit */ +#define RM_ReceiveUnicast (1U << 0) +#define RM_ReceiveMulticast (1U << 1) +#define RM_ReceiveBroadcast (1U << 2) +#define RM_ReceiveAllFrames (1U << 3) +#define RM_ReceiveMulticastHash (1U << 4) +#define RM_ReceiveIPMulticast (1U << 5) +#define RM_ReceiveVLANMatch (1U << 8) +#define RM_ReceiveVLANHash (1U << 9) + +#define STGE_HashTable0 0x8c + +#define STGE_HashTable1 0x90 + +#define STGE_RMONStatisticsMask 0x98 /* set to disable */ + +#define STGE_StatisticsMask 0x9c /* set to disable */ + +#define STGE_RxJumboFrames 0xbc /* 16-bit */ + +#define STGE_TCPCheckSumErrors 0xc0 /* 16-bit */ + +#define STGE_IPCheckSumErrors 0xc2 /* 16-bit */ + +#define STGE_UDPCheckSumErrors 0xc4 /* 16-bit */ + +#define STGE_TxJumboFrames 0xf4 /* 16-bit */ + +/* + * TC9021 statistics. Available memory and I/O mapped. + */ + +#define STGE_OctetRcvOk 0xa8 + +#define STGE_McstOctetRcvdOk 0xac + +#define STGE_BcstOctetRcvdOk 0xb0 + +#define STGE_FramesRcvdOk 0xb4 + +#define STGE_McstFramesRcvdOk 0xb8 + +#define STGE_BcstFramesRcvdOk 0xbe /* 16-bit */ + +#define STGE_MacControlFramesRcvd 0xc6 /* 16-bit */ + +#define STGE_FrameTooLongErrors 0xc8 /* 16-bit */ + +#define STGE_InRangeLengthErrors 0xca /* 16-bit */ + +#define STGE_FramesCheckSeqErrors 0xcc /* 16-bit */ + +#define STGE_FramesLostRxErrors 0xce /* 16-bit */ + +#define STGE_OctetXmtdOk 0xd0 + +#define STGE_McstOctetXmtdOk 0xd4 + +#define STGE_BcstOctetXmtdOk 0xd8 + +#define STGE_FramesXmtdOk 0xdc + +#define STGE_McstFramesXmtdOk 0xe0 + +#define STGE_FramesWDeferredXmt 0xe4 + +#define STGE_LateCollisions 0xe8 + +#define STGE_MultiColFrames 0xec + +#define STGE_SingleColFrames 0xf0 + +#define STGE_BcstFramesXmtdOk 0xf6 /* 16-bit */ + +#define STGE_CarrierSenseErrors 0xf8 /* 16-bit */ + +#define STGE_MacControlFramesXmtd 0xfa /* 16-bit */ + +#define STGE_FramesAbortXSColls 0xfc /* 16-bit */ + +#define STGE_FramesWEXDeferal 0xfe /* 16-bit */ + +/* + * RMON-compatible statistics. Only accessible if memory-mapped. + */ + +#define STGE_EtherStatsCollisions 0x100 + +#define STGE_EtherStatsOctetsTransmit 0x104 + +#define STGE_EtherStatsPktsTransmit 0x108 + +#define STGE_EtherStatsPkts64OctetsTransmit 0x10c + +#define STGE_EtherStatsPkts64to127OctetsTransmit 0x110 + +#define STGE_EtherStatsPkts128to255OctetsTransmit 0x114 + +#define STGE_EtherStatsPkts256to511OctetsTransmit 0x118 + +#define STGE_EtherStatsPkts512to1023OctetsTransmit 0x11c + +#define STGE_EtherStatsPkts1024to1518OctetsTransmit 0x120 + +#define STGE_EtherStatsCRCAlignErrors 0x124 + +#define STGE_EtherStatsUndersizePkts 0x128 + +#define STGE_EtherStatsFragments 0x12c + +#define STGE_EtherStatsJabbers 0x130 + +#define STGE_EtherStatsOctets 0x134 + +#define STGE_EtherStatsPkts 0x138 + +#define STGE_EtherStatsPkts64Octets 0x13c + +#define STGE_EtherStatsPkts65to127Octets 0x140 + +#define STGE_EtherStatsPkts128to255Octets 0x144 + +#define STGE_EtherStatsPkts256to511Octets 0x148 + +#define STGE_EtherStatsPkts512to1023Octets 0x14c + +#define STGE_EtherStatsPkts1024to1518Octets 0x150 + +/* + * Transmit descriptor list size. + */ +#define STGE_TX_RING_CNT 256 +#define STGE_TX_LOWAT (STGE_TX_RING_CNT/32) +#define STGE_TX_HIWAT (STGE_TX_RING_CNT - STGE_TX_LOWAT) + +/* + * Receive descriptor list size. + */ +#define STGE_RX_RING_CNT 256 + +#define STGE_MAXTXSEGS STGE_NTXFRAGS + +#define STGE_JUMBO_FRAMELEN 9022 +#define STGE_JUMBO_MTU \ + (STGE_JUMBO_FRAMELEN - ETHER_HDR_LEN - ETHER_CRC_LEN) + +struct stge_txdesc { + struct mbuf *tx_m; /* head of our mbuf chain */ + bus_dmamap_t tx_dmamap; /* our DMA map */ + STAILQ_ENTRY(stge_txdesc) tx_q; +}; + +STAILQ_HEAD(stge_txdq, stge_txdesc); + +struct stge_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; +}; + +#define STGE_ADDR_LO(x) ((u_int64_t) (x) & 0xffffffff) +#define STGE_ADDR_HI(x) ((u_int64_t) (x) >> 32) + +#define STGE_RING_ALIGN 8 + +struct stge_chain_data{ + bus_dma_tag_t stge_parent_tag; + bus_dma_tag_t stge_tx_tag; + struct stge_txdesc stge_txdesc[STGE_TX_RING_CNT]; + struct stge_txdq stge_txfreeq; + struct stge_txdq stge_txbusyq; + bus_dma_tag_t stge_rx_tag; + struct stge_rxdesc stge_rxdesc[STGE_RX_RING_CNT]; + bus_dma_tag_t stge_tx_ring_tag; + bus_dmamap_t stge_tx_ring_map; + bus_dma_tag_t stge_rx_ring_tag; + bus_dmamap_t stge_rx_ring_map; + bus_dmamap_t stge_rx_sparemap; + + int stge_tx_prod; + int stge_tx_cons; + int stge_tx_cnt; + int stge_rx_cons; +#ifdef DEVICE_POLLING + int stge_rxcycles; +#endif + int stge_rxlen; + struct mbuf *stge_rxhead; + struct mbuf *stge_rxtail; +}; + +struct stge_ring_data { + struct stge_tfd *stge_tx_ring; + bus_addr_t stge_tx_ring_paddr; + struct stge_rfd *stge_rx_ring; + bus_addr_t stge_rx_ring_paddr; +}; + +#define STGE_TX_RING_ADDR(sc, i) \ + ((sc)->sc_rdata.stge_tx_ring_paddr + sizeof(struct stge_tfd) * (i)) +#define STGE_RX_RING_ADDR(sc, i) \ + ((sc)->sc_rdata.stge_rx_ring_paddr + sizeof(struct stge_rfd) * (i)) + +#define STGE_TX_RING_SZ \ + (sizeof(struct stge_tfd) * STGE_TX_RING_CNT) +#define STGE_RX_RING_SZ \ + (sizeof(struct stge_rfd) * STGE_RX_RING_CNT) + +/* + * Software state per device. + */ +struct stge_softc { + struct ifnet *sc_ifp; /* interface info */ + device_t sc_dev; + device_t sc_miibus; + struct resource *sc_res[2]; + struct resource_spec *sc_spec; + void *sc_ih; /* interrupt cookie */ + int sc_rev; /* silicon revision */ + + struct callout sc_tick_ch; /* tick callout */ + + struct stge_chain_data sc_cdata; + struct stge_ring_data sc_rdata; + int sc_if_flags; + int sc_if_framesize; + int sc_txthresh; /* Tx threshold */ + uint32_t sc_usefiber:1; /* if we're fiber */ + uint32_t sc_stge1023:1; /* are we a 1023 */ + uint32_t sc_DMACtrl; /* prototype DMACtrl reg. */ + uint32_t sc_MACCtrl; /* prototype MacCtrl reg. */ + uint16_t sc_IntEnable; /* prototype IntEnable reg. */ + uint16_t sc_led; /* LED conf. from EEPROM */ + uint8_t sc_PhyCtrl; /* prototype PhyCtrl reg. */ + int sc_suspended; + int sc_detach; + + int sc_rxint_nframe; + int sc_rxint_dmawait; + int sc_nerr; + + struct task sc_link_task; + struct mtx sc_mii_mtx; /* MII mutex */ + struct mtx sc_mtx; +}; + +#define STGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define STGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define STGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define STGE_MII_LOCK(_sc) mtx_lock(&(_sc)->sc_mii_mtx) +#define STGE_MII_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mii_mtx) + +#define STGE_MAXERR 5 + +#define STGE_RXCHAIN_RESET(_sc) \ +do { \ + (_sc)->sc_cdata.stge_rxhead = NULL; \ + (_sc)->sc_cdata.stge_rxtail = NULL; \ + (_sc)->sc_cdata.stge_rxlen = 0; \ +} while (/*CONSTCOND*/0) + +#define STGE_TIMEOUT 1000 + +struct stge_mii_frame { + uint8_t mii_stdelim; + uint8_t mii_opcode; + uint8_t mii_phyaddr; + uint8_t mii_regaddr; + uint8_t mii_turnaround; + uint16_t mii_data; +}; + +/* + * MII constants + */ +#define STGE_MII_STARTDELIM 0x01 +#define STGE_MII_READOP 0x02 +#define STGE_MII_WRITEOP 0x01 +#define STGE_MII_TURNAROUND 0x02 + +#define STGE_RESET_NONE 0x00 +#define STGE_RESET_TX 0x01 +#define STGE_RESET_RX 0x02 +#define STGE_RESET_FULL 0x04 diff --git a/sys/geom/eli/g_eli_integrity.c b/sys/geom/eli/g_eli_integrity.c new file mode 100644 index 000000000000..b9a3f31359ad --- /dev/null +++ b/sys/geom/eli/g_eli_integrity.c @@ -0,0 +1,531 @@ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* + * The data layout description when integrity verification is configured. + * + * One of the most important assumption here is that authenticated data and its + * HMAC has to be stored in the same place (namely in the same sector) to make + * it work reliable. + * The problem is that file systems work only with sectors that are multiple of + * 512 bytes and a power of two number. + * My idea to implement it is as follows. + * Let's store HMAC in sector. This is a must. This leaves us 480 bytes for + * data. We can't use that directly (ie. we can't create provider with 480 bytes + * sector size). We need another sector from where we take only 32 bytes of data + * and we store HMAC of this data as well. This takes two sectors from the + * original provider at the input and leaves us one sector of authenticated data + * at the output. Not very efficient, but you got the idea. + * Now, let's assume, we want to create provider with 4096 bytes sector. + * To output 4096 bytes of authenticated data we need 8x480 plus 1x256, so we + * need nine 512-bytes sectors at the input to get one 4096-bytes sector at the + * output. That's better. With 4096 bytes sector we can use 89% of size of the + * original provider. I find it as an acceptable cost. + * The reliability comes from the fact, that every HMAC stored inside the sector + * is calculated only for the data in the same sector, so its impossible to + * write new data and leave old HMAC or vice versa. + * + * And here is the picture: + * + * da0: +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+-----+ + * |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |256b | + * |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data | + * +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+-----+ + * |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |288 bytes | + * +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ |224 unused| + * +----------+ + * da0.eli: +----+----+----+----+----+----+----+----+----+ + * |480b|480b|480b|480b|480b|480b|480b|480b|256b| + * +----+----+----+----+----+----+----+----+----+ + * | 4096 bytes | + * +--------------------------------------------+ + * + * PS. You can use any sector size with geli(8). My example is using 4kB, + * because it's most efficient. For 8kB sectors you need 2 extra sectors, + * so the cost is the same as for 4kB sectors. + */ + +/* + * Code paths: + * BIO_READ: + * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> g_eli_auth_read_done -> g_io_deliver + * BIO_WRITE: + * g_eli_start -> g_eli_auth_run -> g_eli_auth_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver + */ + +MALLOC_DECLARE(M_ELI); + +/* + * Here we generate key for HMAC. Every sector has its own HMAC key, so it is + * not possible to copy sectors. + * We cannot depend on fact, that every sector has its own IV, because different + * IV doesn't change HMAC, when we use encrypt-then-authenticate method. + */ +static void +g_eli_auth_keygen(struct g_eli_softc *sc, off_t offset, u_char *key) +{ + SHA256_CTX ctx; + + /* Copy precalculated SHA256 context. */ + bcopy(&sc->sc_akeyctx, &ctx, sizeof(ctx)); + SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset)); + SHA256_Final(key, &ctx); +} + +/* + * The function is called after we read and decrypt data. + * + * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> G_ELI_AUTH_READ_DONE -> g_io_deliver + */ +static int +g_eli_auth_read_done(struct cryptop *crp) +{ + struct bio *bp; + + if (crp->crp_etype == EAGAIN) { + if (g_eli_crypto_rerun(crp) == 0) + return (0); + } + bp = (struct bio *)crp->crp_opaque; + bp->bio_inbed++; + if (crp->crp_etype == 0) { + bp->bio_completed += crp->crp_olen; + G_ELI_DEBUG(3, "Crypto READ request done (%d/%d) (add=%jd completed=%jd).", + bp->bio_inbed, bp->bio_children, (intmax_t)crp->crp_olen, (intmax_t)bp->bio_completed); + } else { + G_ELI_DEBUG(1, "Crypto READ request failed (%d/%d) error=%d.", + bp->bio_inbed, bp->bio_children, crp->crp_etype); + if (bp->bio_error == 0) + bp->bio_error = crp->crp_etype; + } + /* + * Do we have all sectors already? + */ + if (bp->bio_inbed < bp->bio_children) + return (0); + if (bp->bio_error == 0) { + struct g_eli_softc *sc; + u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; + u_char *srcdata, *dstdata, *auth; + off_t coroff, corsize; + + /* + * Verify data integrity based on calculated and read HMACs. + */ + sc = bp->bio_to->geom->softc; + /* Sectorsize of decrypted provider eg. 4096. */ + decr_secsize = bp->bio_to->sectorsize; + /* The real sectorsize of encrypted provider, eg. 512. */ + encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; + /* Number of data bytes in one encrypted sector, eg. 480. */ + data_secsize = sc->sc_data_per_sector; + /* Number of sectors from decrypted provider, eg. 2. */ + nsec = bp->bio_length / decr_secsize; + /* Number of sectors from encrypted provider, eg. 18. */ + nsec = (nsec * sc->sc_bytes_per_sector) / encr_secsize; + /* Last sector number in every big sector, eg. 9. */ + lsec = sc->sc_bytes_per_sector / encr_secsize; + + srcdata = bp->bio_driver2; + dstdata = bp->bio_data; + auth = srcdata + encr_secsize * nsec; + coroff = -1; + corsize = 0; + + for (i = 1; i <= nsec; i++) { + data_secsize = sc->sc_data_per_sector; + if ((i % lsec) == 0) + data_secsize = decr_secsize % data_secsize; + if (bcmp(srcdata, auth, sc->sc_alen) != 0) { + /* + * Curruption detected, remember the offset if + * this is the first corrupted sector and + * increase size. + */ + if (bp->bio_error == 0) + bp->bio_error = -1; + if (coroff == -1) { + coroff = bp->bio_offset + + (dstdata - (u_char *)bp->bio_data); + } + corsize += data_secsize; + } else { + /* + * No curruption, good. + * Report previous corruption if there was one. + */ + if (coroff != -1) { + G_ELI_DEBUG(0, "%s: %jd bytes " + "corrupted at offset %jd.", + sc->sc_name, (intmax_t)corsize, + (intmax_t)coroff); + coroff = -1; + corsize = 0; + } + bcopy(srcdata + sc->sc_alen, dstdata, + data_secsize); + } + srcdata += encr_secsize; + dstdata += data_secsize; + auth += sc->sc_alen; + } + /* Report previous corruption if there was one. */ + if (coroff != -1) { + G_ELI_DEBUG(0, "%s: %jd bytes corrupted at offset %jd.", + sc->sc_name, (intmax_t)corsize, (intmax_t)coroff); + } + } + free(bp->bio_driver2, M_ELI); + bp->bio_driver2 = NULL; + if (bp->bio_error != 0) { + if (bp->bio_error == -1) + bp->bio_error = EINVAL; + else { + G_ELI_LOGREQ(0, bp, + "Crypto READ request failed (error=%d).", + bp->bio_error); + } + bp->bio_completed = 0; + } + /* + * Read is finished, send it up. + */ + g_io_deliver(bp, bp->bio_error); + return (0); +} + +/* + * The function is called after data encryption. + * + * g_eli_start -> g_eli_auth_run -> G_ELI_AUTH_WRITE_DONE -> g_io_request -> g_eli_write_done -> g_io_deliver + */ +static int +g_eli_auth_write_done(struct cryptop *crp) +{ + struct g_eli_softc *sc; + struct g_consumer *cp; + struct bio *bp, *cbp, *cbp2; + u_int nsec; + + if (crp->crp_etype == EAGAIN) { + if (g_eli_crypto_rerun(crp) == 0) + return (0); + } + bp = (struct bio *)crp->crp_opaque; + bp->bio_inbed++; + if (crp->crp_etype == 0) { + G_ELI_DEBUG(3, "Crypto WRITE request done (%d/%d).", + bp->bio_inbed, bp->bio_children); + } else { + G_ELI_DEBUG(1, "Crypto WRITE request failed (%d/%d) error=%d.", + bp->bio_inbed, bp->bio_children, crp->crp_etype); + if (bp->bio_error == 0) + bp->bio_error = crp->crp_etype; + } + /* + * All sectors are already encrypted? + */ + if (bp->bio_inbed < bp->bio_children) + return (0); + if (bp->bio_error != 0) { + G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", + bp->bio_error); + free(bp->bio_driver2, M_ELI); + bp->bio_driver2 = NULL; + cbp = bp->bio_driver1; + bp->bio_driver1 = NULL; + g_destroy_bio(cbp); + g_io_deliver(bp, bp->bio_error); + return (0); + } + sc = bp->bio_to->geom->softc; + cp = LIST_FIRST(&sc->sc_geom->consumer); + cbp = bp->bio_driver1; + bp->bio_driver1 = NULL; + cbp->bio_to = cp->provider; + cbp->bio_done = g_eli_write_done; + + /* Number of sectors from decrypted provider, eg. 1. */ + nsec = bp->bio_length / bp->bio_to->sectorsize; + /* Number of sectors from encrypted provider, eg. 9. */ + nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; + + cbp->bio_length = cp->provider->sectorsize * nsec; + cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; + cbp->bio_data = bp->bio_driver2; + + /* + * We write more than what is requested, so we have to be ready to write + * more than MAXPHYS. + */ + cbp2 = NULL; + if (cbp->bio_length > MAXPHYS) { + cbp2 = g_duplicate_bio(bp); + cbp2->bio_length = cbp->bio_length - MAXPHYS; + cbp2->bio_data = cbp->bio_data + MAXPHYS; + cbp2->bio_offset = cbp->bio_offset + MAXPHYS; + cbp2->bio_to = cp->provider; + cbp2->bio_done = g_eli_write_done; + cbp->bio_length = MAXPHYS; + } + /* + * Send encrypted data to the provider. + */ + G_ELI_LOGREQ(2, cbp, "Sending request."); + bp->bio_inbed = 0; + bp->bio_children = (cbp2 != NULL ? 2 : 1); + g_io_request(cbp, cp); + if (cbp2 != NULL) { + G_ELI_LOGREQ(2, cbp2, "Sending request."); + g_io_request(cbp2, cp); + } + return (0); +} + +void +g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp) +{ + struct g_consumer *cp; + struct bio *cbp, *cbp2; + size_t size; + off_t nsec; + + bp->bio_pflags = 0; + + cp = LIST_FIRST(&sc->sc_geom->consumer); + cbp = bp->bio_driver1; + bp->bio_driver1 = NULL; + cbp->bio_to = cp->provider; + cbp->bio_done = g_eli_read_done; + + /* Number of sectors from decrypted provider, eg. 1. */ + nsec = bp->bio_length / bp->bio_to->sectorsize; + /* Number of sectors from encrypted provider, eg. 9. */ + nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; + + cbp->bio_length = cp->provider->sectorsize * nsec; + size = cbp->bio_length; + size += sc->sc_alen * nsec; + size += sizeof(struct cryptop) * nsec; + size += sizeof(struct cryptodesc) * nsec * 2; + size += G_ELI_AUTH_SECKEYLEN * nsec; + size += sizeof(struct uio) * nsec; + size += sizeof(struct iovec) * nsec; + cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; + bp->bio_driver2 = malloc(size, M_ELI, M_WAITOK); + cbp->bio_data = bp->bio_driver2; + + /* + * We read more than what is requested, so we have to be ready to read + * more than MAXPHYS. + */ + cbp2 = NULL; + if (cbp->bio_length > MAXPHYS) { + cbp2 = g_duplicate_bio(bp); + cbp2->bio_length = cbp->bio_length - MAXPHYS; + cbp2->bio_data = cbp->bio_data + MAXPHYS; + cbp2->bio_offset = cbp->bio_offset + MAXPHYS; + cbp2->bio_to = cp->provider; + cbp2->bio_done = g_eli_read_done; + cbp->bio_length = MAXPHYS; + } + /* + * Read encrypted data from provider. + */ + G_ELI_LOGREQ(2, cbp, "Sending request."); + g_io_request(cbp, cp); + if (cbp2 != NULL) { + G_ELI_LOGREQ(2, cbp2, "Sending request."); + g_io_request(cbp2, cp); + } +} + +/* + * This is the main function responsible for cryptography (ie. communication + * with crypto(9) subsystem). + */ +void +g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp) +{ + struct g_eli_softc *sc; + struct cryptop *crp; + struct cryptodesc *crde, *crda; + struct uio *uio; + struct iovec *iov; + u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; + off_t dstoff; + int err, error; + u_char *p, *data, *auth, *authkey, *plaindata; + + G_ELI_LOGREQ(3, bp, "%s", __func__); + + bp->bio_pflags = wr->w_number; + sc = wr->w_softc; + /* Sectorsize of decrypted provider eg. 4096. */ + decr_secsize = bp->bio_to->sectorsize; + /* The real sectorsize of encrypted provider, eg. 512. */ + encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; + /* Number of data bytes in one encrypted sector, eg. 480. */ + data_secsize = sc->sc_data_per_sector; + /* Number of sectors from decrypted provider, eg. 2. */ + nsec = bp->bio_length / decr_secsize; + /* Number of sectors from encrypted provider, eg. 18. */ + nsec = (nsec * sc->sc_bytes_per_sector) / encr_secsize; + /* Last sector number in every big sector, eg. 9. */ + lsec = sc->sc_bytes_per_sector / encr_secsize; + /* Destination offset, used for IV generation. */ + dstoff = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; + + auth = NULL; /* Silence compiler warning. */ + plaindata = bp->bio_data; + if (bp->bio_cmd == BIO_READ) { + data = bp->bio_driver2; + auth = data + encr_secsize * nsec; + p = auth + sc->sc_alen * nsec; + } else { + size_t size; + + size = encr_secsize * nsec; + size += sizeof(*crp) * nsec; + size += sizeof(*crde) * nsec; + size += sizeof(*crda) * nsec; + size += G_ELI_AUTH_SECKEYLEN * nsec; + size += sizeof(*uio) * nsec; + size += sizeof(*iov) * nsec; + data = malloc(size, M_ELI, M_WAITOK); + bp->bio_driver2 = data; + p = data + encr_secsize * nsec; + } + bp->bio_inbed = 0; + bp->bio_children = nsec; + + error = 0; + for (i = 1; i <= nsec; i++, dstoff += encr_secsize) { + crp = (struct cryptop *)p; p += sizeof(*crp); + crde = (struct cryptodesc *)p; p += sizeof(*crde); + crda = (struct cryptodesc *)p; p += sizeof(*crda); + authkey = (u_char *)p; p += G_ELI_AUTH_SECKEYLEN; + uio = (struct uio *)p; p += sizeof(*uio); + iov = (struct iovec *)p; p += sizeof(*iov); + + data_secsize = sc->sc_data_per_sector; + if ((i % lsec) == 0) + data_secsize = decr_secsize % data_secsize; + + if (bp->bio_cmd == BIO_READ) { + /* Remember read HMAC. */ + bcopy(data, auth, sc->sc_alen); + auth += sc->sc_alen; + /* TODO: bzero(9) can be commented out later. */ + bzero(data, sc->sc_alen); + } else { + bcopy(plaindata, data + sc->sc_alen, data_secsize); + plaindata += data_secsize; + } + + iov->iov_len = sc->sc_alen + data_secsize; + iov->iov_base = data; + data += encr_secsize; + + uio->uio_iov = iov; + uio->uio_iovcnt = 1; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_resid = iov->iov_len; + + crp->crp_sid = wr->w_sid; + crp->crp_ilen = uio->uio_resid; + crp->crp_olen = data_secsize; + crp->crp_opaque = (void *)bp; + crp->crp_buf = (void *)uio; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC | CRYPTO_F_REL; + if (g_eli_batch) + crp->crp_flags |= CRYPTO_F_BATCH; + if (bp->bio_cmd == BIO_WRITE) { + crp->crp_callback = g_eli_auth_write_done; + crp->crp_desc = crde; + crde->crd_next = crda; + crda->crd_next = NULL; + } else { + crp->crp_callback = g_eli_auth_read_done; + crp->crp_desc = crda; + crda->crd_next = crde; + crde->crd_next = NULL; + } + + crde->crd_skip = sc->sc_alen; + crde->crd_len = data_secsize; + crde->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; + if (bp->bio_cmd == BIO_WRITE) + crde->crd_flags |= CRD_F_ENCRYPT; + crde->crd_alg = sc->sc_ealgo; + crde->crd_key = sc->sc_ekey; + crde->crd_klen = sc->sc_ekeylen; + g_eli_crypto_ivgen(sc, dstoff, crde->crd_iv, + sizeof(crde->crd_iv)); + + crda->crd_skip = sc->sc_alen; + crda->crd_len = data_secsize; + crda->crd_inject = 0; + crda->crd_flags = CRD_F_KEY_EXPLICIT; + crda->crd_alg = sc->sc_aalgo; + g_eli_auth_keygen(sc, dstoff, authkey); + crda->crd_key = authkey; + crda->crd_klen = G_ELI_AUTH_SECKEYLEN * 8; + + crp->crp_etype = 0; + err = crypto_dispatch(crp); + if (err != 0 && error == 0) + error = err; + } + if (bp->bio_error == 0) + bp->bio_error = error; +} diff --git a/sys/modules/stge/Makefile b/sys/modules/stge/Makefile new file mode 100644 index 000000000000..721a42da83e8 --- /dev/null +++ b/sys/modules/stge/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/stge + +KMOD= if_stge +SRCS= if_stge.c device_if.h bus_if.h pci_if.h +SRCS+= miibus_if.h + +.include diff --git a/usr.sbin/sade/globals.c b/usr.sbin/sade/globals.c new file mode 100644 index 000000000000..0b08da3303b1 --- /dev/null +++ b/usr.sbin/sade/globals.c @@ -0,0 +1,84 @@ +/* + * $FreeBSD$ + * + * Copyright (c) 1995 + * Jordan Hubbard. 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, + * verbatim and that no modifications are made prior to this + * point in the file. + * 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 JORDAN HUBBARD ``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 JORDAN HUBBARD OR HIS PETS 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, LIFE 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 "sade.h" + +/* + * Various global variables and an initialization hook to set them to + * whatever values we feel are appropriate. + */ + +int DebugFD; /* Where diagnostic output goes */ +Boolean Fake; /* Only pretend to be useful */ +Boolean DialogActive; /* Is libdialog initialized? */ +Boolean ColorDisplay; /* Are we on a color display? */ +Boolean OnVTY; /* Are we on a VTY? */ +Boolean Restarting; /* Are we restarting sysinstall? */ +Variable *VarHead; /* The head of the variable chain */ +int BootMgr; /* Which boot manager we're using */ +int StatusLine; /* Where to stick our status messages */ +jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */ + +Chunk *HomeChunk; +Chunk *RootChunk; +Chunk *SwapChunk; +Chunk *TmpChunk; +Chunk *UsrChunk; +Chunk *VarChunk; +#ifdef __ia64__ +Chunk *EfiChunk; +#endif + +/* + * Yes, I know some of these are already automatically initialized as + * globals. I simply find it clearer to set everything explicitly. + */ +void +globalsInit(void) +{ + DebugFD = -1; + ColorDisplay = FALSE; + Fake = FALSE; + Restarting = FALSE; + OnVTY = FALSE; + DialogActive = FALSE; + VarHead = NULL; + + HomeChunk = NULL; + RootChunk = NULL; + SwapChunk = NULL; + TmpChunk = NULL; + UsrChunk = NULL; + VarChunk = NULL; +#ifdef __ia64__ + EfiChunk = NULL; +#endif +} diff --git a/usr.sbin/sade/list.h b/usr.sbin/sade/list.h new file mode 100644 index 000000000000..8c5cf2aff0f5 --- /dev/null +++ b/usr.sbin/sade/list.h @@ -0,0 +1,55 @@ +/* + * $FreeBSD$ + * + * Copyright (c) 1997 FreeBSD, Inc. + * 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, + * verbatim and that no modifications are made prior to this + * point in the file. + * 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 PAUL TRAINA ``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 PAUL TRAINA OR HIS KILLER RATS 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, LIFE 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. + * + */ + +/* The structure */ +typedef struct _qelement { + struct _qelement *q_forw; + struct _qelement *q_back; +} qelement; + +#define INITQUE(Xhead) { \ + (Xhead).q_forw = &(Xhead); \ + (Xhead).q_back = &(Xhead); \ +} + +#define EMPTYQUE(Xhead) \ + ((Xhead).q_forw == &(Xhead)) + +#define INSQUEUE(elem, pred) { \ + register qelement *Xe = (qelement *) (elem); \ + register qelement *Xp = (qelement *) (pred); \ + Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \ +} + +#define REMQUE(elem) { \ + register qelement *Xe = (qelement *) (elem); \ + (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \ +} diff --git a/usr.sbin/sade/wizard.c b/usr.sbin/sade/wizard.c new file mode 100644 index 000000000000..5edd872ad99f --- /dev/null +++ b/usr.sbin/sade/wizard.c @@ -0,0 +1,200 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $FreeBSD$ + */ + +#include "sade.h" +#include +#include +#include + +static int +scan_block(int fd, daddr_t block) +{ + u_char foo[512]; + + if (-1 == lseek(fd,block * 512,SEEK_SET)) + err(1,"lseek"); + if (512 != read(fd,foo, 512)) + return 1; + return 0; +} + +static void +Scan_Disk(Disk *d) +{ + char device[64]; + u_long l; + int i,j,fd; + + strcpy(device,"/dev/"); + strcat(device,d->name); + + fd = open(device,O_RDWR); + if (fd < 0) { + msgWarn("open(%s) failed", device); + return; + } + for(i=-1,l=0;;l++) { + j = scan_block(fd,l); + if (j != i) { + if (i == -1) { + printf("%c: %lu.",j ? 'B' : 'G', l); + fflush(stdout); + } else if (i == 0) { + printf(".%lu\nB: %lu.",l-1,l); + fflush(stdout); + } else { + printf(".%lu\nG: %lu.",l-1,l); + fflush(stdout); + } + i = j; + } + } + close(fd); +} + +void +slice_wizard(Disk *d) +{ + Disk *db; + char myprompt[BUFSIZ]; + char input[BUFSIZ]; + char *p,*q=0; + char **cp,*cmds[200]; + int ncmd,i; + + systemSuspendDialog(); + sprintf(myprompt,"%s> ", d->name); + while(1) { + printf("--==##==--\n"); + Debug_Disk(d); + p = CheckRules(d); + if (p) { + printf("%s",p); + free(p); + } + printf("%s", myprompt); + fflush(stdout); + q = p = fgets(input,sizeof(input),stdin); + if(!p) + break; + for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;) + if (**cp != '\0') + cp++; + ncmd = cp - cmds; + if(!ncmd) + continue; + if (!strcasecmp(*cmds,"quit")) { break; } + if (!strcasecmp(*cmds,"exit")) { break; } + if (!strcasecmp(*cmds,"q")) { break; } + if (!strcasecmp(*cmds,"x")) { break; } + if (!strcasecmp(*cmds,"delete") && ncmd == 2) { + printf("delete = %d\n", + Delete_Chunk(d, + (struct chunk *)strtol(cmds[1],0,0))); + continue; + } + if (!strcasecmp(*cmds,"allfreebsd")) { + All_FreeBSD(d, 0); + continue; + } + if (!strcasecmp(*cmds,"dedicate")) { + All_FreeBSD(d, 1); + continue; + } + if (!strcasecmp(*cmds,"bios") && ncmd == 4) { + Set_Bios_Geom(d, + strtol(cmds[1],0,0), + strtol(cmds[2],0,0), + strtol(cmds[3],0,0)); + continue; + } + if (!strcasecmp(*cmds,"list")) { + cp = Disk_Names(); + printf("Disks:"); + for(i=0;cp[i];i++) { + printf(" %s",cp[i]); + free(cp[i]); + } + free(cp); + continue; + } +#ifdef PC98 + if (!strcasecmp(*cmds,"create") && ncmd == 7) { + printf("Create=%d\n", + Create_Chunk(d, + strtol(cmds[1],0,0), + strtol(cmds[2],0,0), + strtol(cmds[3],0,0), + strtol(cmds[4],0,0), + strtol(cmds[5],0,0), + cmds[6])); + continue; + } +#else + if (!strcasecmp(*cmds,"create") && ncmd == 6) { + printf("Create=%d\n", + Create_Chunk(d, + strtol(cmds[1],0,0), + strtol(cmds[2],0,0), + strtol(cmds[3],0,0), + strtol(cmds[4],0,0), + strtol(cmds[5],0,0), "")); + continue; + } +#endif + if (!strcasecmp(*cmds,"read")) { + db = d; + if (ncmd > 1) + d = Open_Disk(cmds[1]); + else + d = Open_Disk(d->name); + if (d) + Free_Disk(db); + else + d = db; + continue; + } + if (!strcasecmp(*cmds,"scan")) { + Scan_Disk(d); + continue; + } + if (!strcasecmp(*cmds,"write")) { + printf("Write=%d\n", + Fake ? 0 : Write_Disk(d)); + q = strdup(d->name); + Free_Disk(d); + d = Open_Disk(q); + continue; + } + if (strcasecmp(*cmds,"help")) + printf("\007ERROR\n"); + printf("CMDS:\n"); + printf("allfreebsd\t\t"); + printf("dedicate\t\t"); + printf("bios cyl hd sect\n"); + printf("collapse [pointer]\t\t"); +#ifdef PC98 + printf("create offset size enum subtype flags name\n"); +#else + printf("create offset size enum subtype flags\n"); +#endif + printf("subtype(part): swap=1, ffs=7\t\t"); + printf("delete pointer\n"); + printf("list\t\t"); + printf("quit\n"); + printf("read [disk]\t\t"); + printf("scan\n"); + printf("write\t\t"); + printf("\n"); + + } + systemResumeDialog(); +}